More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 41 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Set Gov | 8438993 | 473 days ago | IN | 0 ETH | 0.00006246 | ||||
Set Gov | 1762397 | 634 days ago | IN | 0 ETH | 0.00006787 | ||||
Set Liquidator | 1762393 | 634 days ago | IN | 0 ETH | 0.00011164 | ||||
Set Gov | 1762206 | 634 days ago | IN | 0 ETH | 0.00006057 | ||||
Set Liquidator | 1762192 | 634 days ago | IN | 0 ETH | 0.00010142 | ||||
Set Gov | 607541 | 661 days ago | IN | 0 ETH | 0.00003272 | ||||
Set In Private L... | 607536 | 661 days ago | IN | 0 ETH | 0.00003533 | ||||
Set Gov | 592574 | 661 days ago | IN | 0 ETH | 0.00002947 | ||||
Set Fees | 592500 | 661 days ago | IN | 0 ETH | 0.00005667 | ||||
Liquidate Positi... | 591585 | 661 days ago | IN | 0 ETH | 0.00026465 | ||||
Liquidate Positi... | 591319 | 661 days ago | IN | 0 ETH | 0.00019806 | ||||
Set Gov | 591197 | 661 days ago | IN | 0 ETH | 0.00003434 | ||||
Set Fees | 591196 | 661 days ago | IN | 0 ETH | 0.0000616 | ||||
Liquidate Positi... | 524964 | 665 days ago | IN | 0 ETH | 0.0002972 | ||||
Liquidate Positi... | 497777 | 666 days ago | IN | 0 ETH | 0.00035705 | ||||
Liquidate Positi... | 379026 | 670 days ago | IN | 0 ETH | 0.00044627 | ||||
Liquidate Positi... | 308982 | 675 days ago | IN | 0 ETH | 0.00067307 | ||||
Liquidate Positi... | 285542 | 677 days ago | IN | 0 ETH | 0.00091954 | ||||
Set Gov | 282124 | 677 days ago | IN | 0 ETH | 0.0001298 | ||||
Set Token Config | 282121 | 677 days ago | IN | 0 ETH | 0.00048375 | ||||
Set Token Config | 282097 | 677 days ago | IN | 0 ETH | 0.00092896 | ||||
Set Token Config | 282079 | 677 days ago | IN | 0 ETH | 0.00091925 | ||||
Set Token Config | 282072 | 677 days ago | IN | 0 ETH | 0.00122122 | ||||
Set Token Config | 282070 | 677 days ago | IN | 0 ETH | 0.00054214 | ||||
Set Token Config | 282068 | 677 days ago | IN | 0 ETH | 0.00122124 |
Loading...
Loading
Contract Name:
Vault
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../tokens/interfaces/IUSDG.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IVaultUtils.sol"; import "./interfaces/IVaultPriceFeed.sol"; contract Vault is ReentrancyGuard, IVault { using SafeMath for uint256; using SafeERC20 for IERC20; struct Position { uint256 size; uint256 collateral; uint256 averagePrice; uint256 entryFundingRate; uint256 reserveAmount; int256 realisedPnl; uint256 lastIncreasedTime; } uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant FUNDING_RATE_PRECISION = 1000000; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant MIN_LEVERAGE = 10000; // 1x uint256 public constant USDG_DECIMALS = 18; uint256 public constant MAX_FEE_BASIS_POINTS = 500; // 5% uint256 public constant MAX_LIQUIDATION_FEE_USD = 100 * PRICE_PRECISION; // 100 USD uint256 public constant MIN_FUNDING_RATE_INTERVAL = 1 hours; uint256 public constant MAX_FUNDING_RATE_FACTOR = 10000; // 1% bool public override isInitialized; bool public override isSwapEnabled = true; bool public override isLeverageEnabled = true; IVaultUtils public vaultUtils; address public errorController; address public override router; address public override priceFeed; address public override usdg; address public override gov; uint256 public override whitelistedTokenCount; uint256 public override maxLeverage = 50 * 10000; // 50x uint256 public override liquidationFeeUsd; uint256 public override taxBasisPoints = 50; // 0.5% uint256 public override stableTaxBasisPoints = 20; // 0.2% uint256 public override mintBurnFeeBasisPoints = 30; // 0.3% uint256 public override swapFeeBasisPoints = 30; // 0.3% uint256 public override stableSwapFeeBasisPoints = 4; // 0.04% uint256 public override marginFeeBasisPoints = 10; // 0.1% uint256 public override minProfitTime; bool public override hasDynamicFees = false; uint256 public override fundingInterval = 8 hours; uint256 public override fundingRateFactor; uint256 public override stableFundingRateFactor; uint256 public override totalTokenWeights; bool public includeAmmPrice = true; bool public useSwapPricing = false; bool public override inManagerMode = false; bool public override inPrivateLiquidationMode = false; uint256 public override maxGasPrice; mapping (address => mapping (address => bool)) public override approvedRouters; mapping (address => bool) public override isLiquidator; mapping (address => bool) public override isManager; address[] public override allWhitelistedTokens; mapping (address => bool) public override whitelistedTokens; mapping (address => uint256) public override tokenDecimals; mapping (address => uint256) public override minProfitBasisPoints; mapping (address => bool) public override stableTokens; mapping (address => bool) public override shortableTokens; // tokenBalances is used only to determine _transferIn values mapping (address => uint256) public override tokenBalances; // tokenWeights allows customisation of index composition mapping (address => uint256) public override tokenWeights; // usdgAmounts tracks the amount of USDG debt for each whitelisted token mapping (address => uint256) public override usdgAmounts; // maxUsdgAmounts allows setting a max amount of USDG debt for a token mapping (address => uint256) public override maxUsdgAmounts; // poolAmounts tracks the number of received tokens that can be used for leverage // this is tracked separately from tokenBalances to exclude funds that are deposited as margin collateral mapping (address => uint256) public override poolAmounts; // reservedAmounts tracks the number of tokens reserved for open leverage positions mapping (address => uint256) public override reservedAmounts; // bufferAmounts allows specification of an amount to exclude from swaps // this can be used to ensure a certain amount of liquidity is available for leverage positions mapping (address => uint256) public override bufferAmounts; // guaranteedUsd tracks the amount of USD that is "guaranteed" by opened leverage positions // this value is used to calculate the redemption values for selling of USDG // this is an estimated amount, it is possible for the actual guaranteed value to be lower // in the case of sudden price decreases, the guaranteed value should be corrected // after liquidations are carried out mapping (address => uint256) public override guaranteedUsd; // cumulativeFundingRates tracks the funding rates based on utilization mapping (address => uint256) public override cumulativeFundingRates; // lastFundingTimes tracks the last time funding was updated for a token mapping (address => uint256) public override lastFundingTimes; // positions tracks all open positions mapping (bytes32 => Position) public positions; // feeReserves tracks the amount of fees per token mapping (address => uint256) public override feeReserves; mapping (address => uint256) public override globalShortSizes; mapping (address => uint256) public override globalShortAveragePrices; mapping (address => uint256) public override maxGlobalShortSizes; mapping (uint256 => string) public errors; event BuyUSDG(address account, address token, uint256 tokenAmount, uint256 usdgAmount, uint256 feeBasisPoints); event SellUSDG(address account, address token, uint256 usdgAmount, uint256 tokenAmount, uint256 feeBasisPoints); event Swap(address account, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, uint256 amountOutAfterFees, uint256 feeBasisPoints); event IncreasePosition( bytes32 key, address account, address collateralToken, address indexToken, uint256 collateralDelta, uint256 sizeDelta, bool isLong, uint256 price, uint256 fee ); event DecreasePosition( bytes32 key, address account, address collateralToken, address indexToken, uint256 collateralDelta, uint256 sizeDelta, bool isLong, uint256 price, uint256 fee ); event LiquidatePosition( bytes32 key, address account, address collateralToken, address indexToken, bool isLong, uint256 size, uint256 collateral, uint256 reserveAmount, int256 realisedPnl, uint256 markPrice ); event UpdatePosition( bytes32 key, uint256 size, uint256 collateral, uint256 averagePrice, uint256 entryFundingRate, uint256 reserveAmount, int256 realisedPnl, uint256 markPrice ); event ClosePosition( bytes32 key, uint256 size, uint256 collateral, uint256 averagePrice, uint256 entryFundingRate, uint256 reserveAmount, int256 realisedPnl ); event UpdateFundingRate(address token, uint256 fundingRate); event UpdatePnl(bytes32 key, bool hasProfit, uint256 delta); event CollectSwapFees(address token, uint256 feeUsd, uint256 feeTokens); event CollectMarginFees(address token, uint256 feeUsd, uint256 feeTokens); event DirectPoolDeposit(address token, uint256 amount); event IncreasePoolAmount(address token, uint256 amount); event DecreasePoolAmount(address token, uint256 amount); event IncreaseUsdgAmount(address token, uint256 amount); event DecreaseUsdgAmount(address token, uint256 amount); event IncreaseReservedAmount(address token, uint256 amount); event DecreaseReservedAmount(address token, uint256 amount); event IncreaseGuaranteedUsd(address token, uint256 amount); event DecreaseGuaranteedUsd(address token, uint256 amount); // once the parameters are verified to be working correctly, // gov should be set to a timelock contract or a governance contract constructor() public { gov = msg.sender; } function initialize( address _router, address _usdg, address _priceFeed, uint256 _liquidationFeeUsd, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor ) external { _onlyGov(); _validate(!isInitialized, 1); isInitialized = true; router = _router; usdg = _usdg; priceFeed = _priceFeed; liquidationFeeUsd = _liquidationFeeUsd; fundingRateFactor = _fundingRateFactor; stableFundingRateFactor = _stableFundingRateFactor; } function setVaultUtils(IVaultUtils _vaultUtils) external override { _onlyGov(); vaultUtils = _vaultUtils; } function setErrorController(address _errorController) external { _onlyGov(); errorController = _errorController; } function setError(uint256 _errorCode, string calldata _error) external override { require(msg.sender == errorController, "Vault: invalid errorController"); errors[_errorCode] = _error; } function allWhitelistedTokensLength() external override view returns (uint256) { return allWhitelistedTokens.length; } function setInManagerMode(bool _inManagerMode) external override { _onlyGov(); inManagerMode = _inManagerMode; } function setManager(address _manager, bool _isManager) external override { _onlyGov(); isManager[_manager] = _isManager; } function setInPrivateLiquidationMode(bool _inPrivateLiquidationMode) external override { _onlyGov(); inPrivateLiquidationMode = _inPrivateLiquidationMode; } function setLiquidator(address _liquidator, bool _isActive) external override { _onlyGov(); isLiquidator[_liquidator] = _isActive; } function setIsSwapEnabled(bool _isSwapEnabled) external override { _onlyGov(); isSwapEnabled = _isSwapEnabled; } function setIsLeverageEnabled(bool _isLeverageEnabled) external override { _onlyGov(); isLeverageEnabled = _isLeverageEnabled; } function setMaxGasPrice(uint256 _maxGasPrice) external override { _onlyGov(); maxGasPrice = _maxGasPrice; } function setGov(address _gov) external { _onlyGov(); gov = _gov; } function setPriceFeed(address _priceFeed) external override { _onlyGov(); priceFeed = _priceFeed; } function setMaxLeverage(uint256 _maxLeverage) external override { _onlyGov(); _validate(_maxLeverage > MIN_LEVERAGE, 2); maxLeverage = _maxLeverage; } function setBufferAmount(address _token, uint256 _amount) external override { _onlyGov(); bufferAmounts[_token] = _amount; } function setMaxGlobalShortSize(address _token, uint256 _amount) external override { _onlyGov(); maxGlobalShortSizes[_token] = _amount; } function setFees( uint256 _taxBasisPoints, uint256 _stableTaxBasisPoints, uint256 _mintBurnFeeBasisPoints, uint256 _swapFeeBasisPoints, uint256 _stableSwapFeeBasisPoints, uint256 _marginFeeBasisPoints, uint256 _liquidationFeeUsd, uint256 _minProfitTime, bool _hasDynamicFees ) external override { _onlyGov(); _validate(_taxBasisPoints <= MAX_FEE_BASIS_POINTS, 3); _validate(_stableTaxBasisPoints <= MAX_FEE_BASIS_POINTS, 4); _validate(_mintBurnFeeBasisPoints <= MAX_FEE_BASIS_POINTS, 5); _validate(_swapFeeBasisPoints <= MAX_FEE_BASIS_POINTS, 6); _validate(_stableSwapFeeBasisPoints <= MAX_FEE_BASIS_POINTS, 7); _validate(_marginFeeBasisPoints <= MAX_FEE_BASIS_POINTS, 8); _validate(_liquidationFeeUsd <= MAX_LIQUIDATION_FEE_USD, 9); taxBasisPoints = _taxBasisPoints; stableTaxBasisPoints = _stableTaxBasisPoints; mintBurnFeeBasisPoints = _mintBurnFeeBasisPoints; swapFeeBasisPoints = _swapFeeBasisPoints; stableSwapFeeBasisPoints = _stableSwapFeeBasisPoints; marginFeeBasisPoints = _marginFeeBasisPoints; liquidationFeeUsd = _liquidationFeeUsd; minProfitTime = _minProfitTime; hasDynamicFees = _hasDynamicFees; } function setFundingRate(uint256 _fundingInterval, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor) external override { _onlyGov(); _validate(_fundingInterval >= MIN_FUNDING_RATE_INTERVAL, 10); _validate(_fundingRateFactor <= MAX_FUNDING_RATE_FACTOR, 11); _validate(_stableFundingRateFactor <= MAX_FUNDING_RATE_FACTOR, 12); fundingInterval = _fundingInterval; fundingRateFactor = _fundingRateFactor; stableFundingRateFactor = _stableFundingRateFactor; } function setTokenConfig( address _token, uint256 _tokenDecimals, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external override { _onlyGov(); // increment token count for the first time if (!whitelistedTokens[_token]) { whitelistedTokenCount = whitelistedTokenCount.add(1); allWhitelistedTokens.push(_token); } uint256 _totalTokenWeights = totalTokenWeights; _totalTokenWeights = _totalTokenWeights.sub(tokenWeights[_token]); whitelistedTokens[_token] = true; tokenDecimals[_token] = _tokenDecimals; tokenWeights[_token] = _tokenWeight; minProfitBasisPoints[_token] = _minProfitBps; maxUsdgAmounts[_token] = _maxUsdgAmount; stableTokens[_token] = _isStable; shortableTokens[_token] = _isShortable; //what is shortable?. looks like if is stablecoin, then not shortable, // and if not stablecoin, then shortable totalTokenWeights = _totalTokenWeights.add(_tokenWeight); // validate price feed getMaxPrice(_token); } function clearTokenConfig(address _token) external { _onlyGov(); _validate(whitelistedTokens[_token], 13); totalTokenWeights = totalTokenWeights.sub(tokenWeights[_token]); delete whitelistedTokens[_token]; delete tokenDecimals[_token]; delete tokenWeights[_token]; delete minProfitBasisPoints[_token]; delete maxUsdgAmounts[_token]; delete stableTokens[_token]; delete shortableTokens[_token]; whitelistedTokenCount = whitelistedTokenCount.sub(1); } function withdrawFees(address _token, address _receiver) external override returns (uint256) { _onlyGov(); uint256 amount = feeReserves[_token]; if(amount == 0) { return 0; } feeReserves[_token] = 0; _transferOut(_token, amount, _receiver); return amount; } function addRouter(address _router) external { approvedRouters[msg.sender][_router] = true; } function removeRouter(address _router) external { approvedRouters[msg.sender][_router] = false; } function setUsdgAmount(address _token, uint256 _amount) external override { _onlyGov(); uint256 usdgAmount = usdgAmounts[_token]; if (_amount > usdgAmount) { _increaseUsdgAmount(_token, _amount.sub(usdgAmount)); return; } _decreaseUsdgAmount(_token, usdgAmount.sub(_amount)); } // the governance controlling this function should have a timelock function upgradeVault(address _newVault, address _token, uint256 _amount) external { _onlyGov(); IERC20(_token).safeTransfer(_newVault, _amount); } // deposit into the pool without minting USDG tokens // useful in allowing the pool to become over-collaterised function directPoolDeposit(address _token) external override nonReentrant { _validate(whitelistedTokens[_token], 14); uint256 tokenAmount = _transferIn(_token); _validate(tokenAmount > 0, 15); _increasePoolAmount(_token, tokenAmount); emit DirectPoolDeposit(_token, tokenAmount); } function buyUSDG(address _token, address _receiver) external override nonReentrant returns (uint256) { _validateManager(); _validate(whitelistedTokens[_token], 16); useSwapPricing = true; uint256 tokenAmount = _transferIn(_token); _validate(tokenAmount > 0, 17); updateCumulativeFundingRate(_token, _token); uint256 price = getMinPrice(_token); uint256 usdgAmount = tokenAmount.mul(price).div(PRICE_PRECISION); usdgAmount = adjustForDecimals(usdgAmount, _token, usdg); _validate(usdgAmount > 0, 18); uint256 feeBasisPoints = vaultUtils.getBuyUsdgFeeBasisPoints(_token, usdgAmount); uint256 amountAfterFees = _collectSwapFees(_token, tokenAmount, feeBasisPoints); uint256 mintAmount = amountAfterFees.mul(price).div(PRICE_PRECISION); mintAmount = adjustForDecimals(mintAmount, _token, usdg); _increaseUsdgAmount(_token, mintAmount); _increasePoolAmount(_token, amountAfterFees); IUSDG(usdg).mint(_receiver, mintAmount); emit BuyUSDG(_receiver, _token, tokenAmount, mintAmount, feeBasisPoints); useSwapPricing = false; return mintAmount; } function sellUSDG(address _token, address _receiver) external override nonReentrant returns (uint256) { _validateManager(); _validate(whitelistedTokens[_token], 19); useSwapPricing = true; uint256 usdgAmount = _transferIn(usdg); _validate(usdgAmount > 0, 20); updateCumulativeFundingRate(_token, _token); uint256 redemptionAmount = getRedemptionAmount(_token, usdgAmount); _validate(redemptionAmount > 0, 21); _decreaseUsdgAmount(_token, usdgAmount); _decreasePoolAmount(_token, redemptionAmount); IUSDG(usdg).burn(address(this), usdgAmount); // the _transferIn call increased the value of tokenBalances[usdg] // usually decreases in token balances are synced by calling _transferOut // however, for usdg, the tokens are burnt, so _updateTokenBalance should // be manually called to record the decrease in tokens _updateTokenBalance(usdg); uint256 feeBasisPoints = vaultUtils.getSellUsdgFeeBasisPoints(_token, usdgAmount); uint256 amountOut = _collectSwapFees(_token, redemptionAmount, feeBasisPoints); _validate(amountOut > 0, 22); _transferOut(_token, amountOut, _receiver); emit SellUSDG(_receiver, _token, usdgAmount, amountOut, feeBasisPoints); useSwapPricing = false; return amountOut; } function swap(address _tokenIn, address _tokenOut, address _receiver) external override nonReentrant returns (uint256) { _validate(isSwapEnabled, 23); _validate(whitelistedTokens[_tokenIn], 24); _validate(whitelistedTokens[_tokenOut], 25); _validate(_tokenIn != _tokenOut, 26); useSwapPricing = true; updateCumulativeFundingRate(_tokenIn, _tokenIn); updateCumulativeFundingRate(_tokenOut, _tokenOut); uint256 amountIn = _transferIn(_tokenIn); _validate(amountIn > 0, 27); uint256 priceIn = getMinPrice(_tokenIn); uint256 priceOut = getMaxPrice(_tokenOut); uint256 amountOut = amountIn.mul(priceIn).div(priceOut); amountOut = adjustForDecimals(amountOut, _tokenIn, _tokenOut); // adjust usdgAmounts by the same usdgAmount as debt is shifted between the assets uint256 usdgAmount = amountIn.mul(priceIn).div(PRICE_PRECISION); usdgAmount = adjustForDecimals(usdgAmount, _tokenIn, usdg); uint256 feeBasisPoints = vaultUtils.getSwapFeeBasisPoints(_tokenIn, _tokenOut, usdgAmount); uint256 amountOutAfterFees = _collectSwapFees(_tokenOut, amountOut, feeBasisPoints); _increaseUsdgAmount(_tokenIn, usdgAmount); _decreaseUsdgAmount(_tokenOut, usdgAmount); _increasePoolAmount(_tokenIn, amountIn); _decreasePoolAmount(_tokenOut, amountOut); _validateBufferAmount(_tokenOut); _transferOut(_tokenOut, amountOutAfterFees, _receiver); emit Swap(_receiver, _tokenIn, _tokenOut, amountIn, amountOut, amountOutAfterFees, feeBasisPoints); useSwapPricing = false; return amountOutAfterFees; } function increasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external override nonReentrant { _validate(isLeverageEnabled, 28); _validateGasPrice(); _validateRouter(_account); _validateTokens(_collateralToken, _indexToken, _isLong); vaultUtils.validateIncreasePosition(_account, _collateralToken, _indexToken, _sizeDelta, _isLong); updateCumulativeFundingRate(_collateralToken, _indexToken); bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position storage position = positions[key]; uint256 price = _isLong ? getMaxPrice(_indexToken) : getMinPrice(_indexToken); if (position.size == 0) { position.averagePrice = price; } if (position.size > 0 && _sizeDelta > 0) { position.averagePrice = getNextAveragePrice(_indexToken, position.size, position.averagePrice, _isLong, price, _sizeDelta, position.lastIncreasedTime); } uint256 fee = _collectMarginFees(_account, _collateralToken, _indexToken, _isLong, _sizeDelta, position.size, position.entryFundingRate); uint256 collateralDelta = _transferIn(_collateralToken); uint256 collateralDeltaUsd = tokenToUsdMin(_collateralToken, collateralDelta); position.collateral = position.collateral.add(collateralDeltaUsd); _validate(position.collateral >= fee, 29); position.collateral = position.collateral.sub(fee); position.entryFundingRate = getEntryFundingRate(_collateralToken, _indexToken, _isLong); position.size = position.size.add(_sizeDelta); position.lastIncreasedTime = block.timestamp; _validate(position.size > 0, 30); _validatePosition(position.size, position.collateral); validateLiquidation(_account, _collateralToken, _indexToken, _isLong, true); // reserve tokens to pay profits on the position uint256 reserveDelta = usdToTokenMax(_collateralToken, _sizeDelta); position.reserveAmount = position.reserveAmount.add(reserveDelta); _increaseReservedAmount(_collateralToken, reserveDelta); if (_isLong) { // guaranteedUsd stores the sum of (position.size - position.collateral) for all positions // if a fee is charged on the collateral then guaranteedUsd should be increased by that fee amount // since (position.size - position.collateral) would have increased by `fee` _increaseGuaranteedUsd(_collateralToken, _sizeDelta.add(fee)); _decreaseGuaranteedUsd(_collateralToken, collateralDeltaUsd); // treat the deposited collateral as part of the pool _increasePoolAmount(_collateralToken, collateralDelta); // fees need to be deducted from the pool since fees are deducted from position.collateral // and collateral is treated as part of the pool _decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, fee)); } else { if (globalShortSizes[_indexToken] == 0) { globalShortAveragePrices[_indexToken] = price; } else { globalShortAveragePrices[_indexToken] = getNextGlobalShortAveragePrice(_indexToken, price, _sizeDelta); } _increaseGlobalShortSize(_indexToken, _sizeDelta); } emit IncreasePosition(key, _account, _collateralToken, _indexToken, collateralDeltaUsd, _sizeDelta, _isLong, price, fee); emit UpdatePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl, price); } function decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external override nonReentrant returns (uint256) { _validateGasPrice(); _validateRouter(_account); return _decreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver); } function _decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) private returns (uint256) { vaultUtils.validateDecreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver); updateCumulativeFundingRate(_collateralToken, _indexToken); bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position storage position = positions[key]; _validate(position.size > 0, 31); _validate(position.size >= _sizeDelta, 32); _validate(position.collateral >= _collateralDelta, 33); uint256 collateral = position.collateral; // scrop variables to avoid stack too deep errors { uint256 reserveDelta = position.reserveAmount.mul(_sizeDelta).div(position.size); position.reserveAmount = position.reserveAmount.sub(reserveDelta); _decreaseReservedAmount(_collateralToken, reserveDelta); } (uint256 usdOut, uint256 usdOutAfterFee) = _reduceCollateral(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong); if (position.size != _sizeDelta) { position.entryFundingRate = getEntryFundingRate(_collateralToken, _indexToken, _isLong); position.size = position.size.sub(_sizeDelta); _validatePosition(position.size, position.collateral); validateLiquidation(_account, _collateralToken, _indexToken, _isLong, true); if (_isLong) { _increaseGuaranteedUsd(_collateralToken, collateral.sub(position.collateral)); _decreaseGuaranteedUsd(_collateralToken, _sizeDelta); } uint256 price = _isLong ? getMinPrice(_indexToken) : getMaxPrice(_indexToken); emit DecreasePosition(key, _account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, price, usdOut.sub(usdOutAfterFee)); emit UpdatePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl, price); } else { if (_isLong) { _increaseGuaranteedUsd(_collateralToken, collateral); _decreaseGuaranteedUsd(_collateralToken, _sizeDelta); } uint256 price = _isLong ? getMinPrice(_indexToken) : getMaxPrice(_indexToken); emit DecreasePosition(key, _account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, price, usdOut.sub(usdOutAfterFee)); emit ClosePosition(key, position.size, position.collateral, position.averagePrice, position.entryFundingRate, position.reserveAmount, position.realisedPnl); delete positions[key]; } if (!_isLong) { _decreaseGlobalShortSize(_indexToken, _sizeDelta); } if (usdOut > 0) { if (_isLong) { _decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, usdOut)); } uint256 amountOutAfterFees = usdToTokenMin(_collateralToken, usdOutAfterFee); _transferOut(_collateralToken, amountOutAfterFees, _receiver); return amountOutAfterFees; } return 0; } function liquidatePosition(address _account, address _collateralToken, address _indexToken, bool _isLong, address _feeReceiver) external override nonReentrant { if (inPrivateLiquidationMode) { _validate(isLiquidator[msg.sender], 34); } // set includeAmmPrice to false to prevent manipulated liquidations includeAmmPrice = false; updateCumulativeFundingRate(_collateralToken, _indexToken); bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position memory position = positions[key]; _validate(position.size > 0, 35); (uint256 liquidationState, uint256 marginFees) = validateLiquidation(_account, _collateralToken, _indexToken, _isLong, false); _validate(liquidationState != 0, 36); if (liquidationState == 2) { // max leverage exceeded but there is collateral remaining after deducting losses so decreasePosition instead _decreasePosition(_account, _collateralToken, _indexToken, 0, position.size, _isLong, _account); includeAmmPrice = true; return; } uint256 feeTokens = usdToTokenMin(_collateralToken, marginFees); feeReserves[_collateralToken] = feeReserves[_collateralToken].add(feeTokens); emit CollectMarginFees(_collateralToken, marginFees, feeTokens); _decreaseReservedAmount(_collateralToken, position.reserveAmount); if (_isLong) { _decreaseGuaranteedUsd(_collateralToken, position.size.sub(position.collateral)); _decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, marginFees)); } uint256 markPrice = _isLong ? getMinPrice(_indexToken) : getMaxPrice(_indexToken); emit LiquidatePosition(key, _account, _collateralToken, _indexToken, _isLong, position.size, position.collateral, position.reserveAmount, position.realisedPnl, markPrice); if (!_isLong && marginFees < position.collateral) { uint256 remainingCollateral = position.collateral.sub(marginFees); _increasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, remainingCollateral)); } if (!_isLong) { _decreaseGlobalShortSize(_indexToken, position.size); } delete positions[key]; // pay the fee receiver using the pool, we assume that in general the liquidated amount should be sufficient to cover // the liquidation fees _decreasePoolAmount(_collateralToken, usdToTokenMin(_collateralToken, liquidationFeeUsd)); _transferOut(_collateralToken, usdToTokenMin(_collateralToken, liquidationFeeUsd), _feeReceiver); includeAmmPrice = true; } // validateLiquidation returns (state, fees) function validateLiquidation(address _account, address _collateralToken, address _indexToken, bool _isLong, bool _raise) override public view returns (uint256, uint256) { return vaultUtils.validateLiquidation(_account, _collateralToken, _indexToken, _isLong, _raise); } function getMaxPrice(address _token) public override view returns (uint256) { return IVaultPriceFeed(priceFeed).getPrice(_token, true, includeAmmPrice, useSwapPricing); } function getMinPrice(address _token) public override view returns (uint256) { return IVaultPriceFeed(priceFeed).getPrice(_token, false, includeAmmPrice, useSwapPricing); } function getRedemptionAmount(address _token, uint256 _usdgAmount) public override view returns (uint256) { uint256 price = getMaxPrice(_token); uint256 redemptionAmount = _usdgAmount.mul(PRICE_PRECISION).div(price); return adjustForDecimals(redemptionAmount, usdg, _token); } function getRedemptionCollateral(address _token) public view returns (uint256) { if (stableTokens[_token]) { return poolAmounts[_token]; } uint256 collateral = usdToTokenMin(_token, guaranteedUsd[_token]); return collateral.add(poolAmounts[_token]).sub(reservedAmounts[_token]); } function getRedemptionCollateralUsd(address _token) public view returns (uint256) { return tokenToUsdMin(_token, getRedemptionCollateral(_token)); } function adjustForDecimals(uint256 _amount, address _tokenDiv, address _tokenMul) public view returns (uint256) { uint256 decimalsDiv = _tokenDiv == usdg ? USDG_DECIMALS : tokenDecimals[_tokenDiv]; uint256 decimalsMul = _tokenMul == usdg ? USDG_DECIMALS : tokenDecimals[_tokenMul]; return _amount.mul(10 ** decimalsMul).div(10 ** decimalsDiv); } function tokenToUsdMin(address _token, uint256 _tokenAmount) public override view returns (uint256) { if (_tokenAmount == 0) { return 0; } uint256 price = getMinPrice(_token); uint256 decimals = tokenDecimals[_token]; return _tokenAmount.mul(price).div(10 ** decimals); } function usdToTokenMax(address _token, uint256 _usdAmount) public view returns (uint256) { if (_usdAmount == 0) { return 0; } return usdToToken(_token, _usdAmount, getMinPrice(_token)); } function usdToTokenMin(address _token, uint256 _usdAmount) public view returns (uint256) { if (_usdAmount == 0) { return 0; } return usdToToken(_token, _usdAmount, getMaxPrice(_token)); } function usdToToken(address _token, uint256 _usdAmount, uint256 _price) public view returns (uint256) { if (_usdAmount == 0) { return 0; } uint256 decimals = tokenDecimals[_token]; return _usdAmount.mul(10 ** decimals).div(_price); } function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong) public override view returns (uint256, uint256, uint256, uint256, uint256, uint256, bool, uint256) { bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position memory position = positions[key]; uint256 realisedPnl = position.realisedPnl > 0 ? uint256(position.realisedPnl) : uint256(-position.realisedPnl); return ( position.size, // 0 position.collateral, // 1 position.averagePrice, // 2 position.entryFundingRate, // 3 position.reserveAmount, // 4 realisedPnl, // 5 position.realisedPnl >= 0, // 6 position.lastIncreasedTime // 7 ); } function getPositionKey(address _account, address _collateralToken, address _indexToken, bool _isLong) public pure returns (bytes32) { return keccak256(abi.encodePacked( _account, _collateralToken, _indexToken, _isLong )); } function updateCumulativeFundingRate(address _collateralToken, address _indexToken) public { bool shouldUpdate = vaultUtils.updateCumulativeFundingRate(_collateralToken, _indexToken); if (!shouldUpdate) { return; } if (lastFundingTimes[_collateralToken] == 0) { lastFundingTimes[_collateralToken] = block.timestamp.div(fundingInterval).mul(fundingInterval); return; } if (lastFundingTimes[_collateralToken].add(fundingInterval) > block.timestamp) { return; } uint256 fundingRate = getNextFundingRate(_collateralToken); cumulativeFundingRates[_collateralToken] = cumulativeFundingRates[_collateralToken].add(fundingRate); lastFundingTimes[_collateralToken] = block.timestamp.div(fundingInterval).mul(fundingInterval); emit UpdateFundingRate(_collateralToken, cumulativeFundingRates[_collateralToken]); } function getNextFundingRate(address _token) public override view returns (uint256) { if (lastFundingTimes[_token].add(fundingInterval) > block.timestamp) { return 0; } uint256 intervals = block.timestamp.sub(lastFundingTimes[_token]).div(fundingInterval); uint256 poolAmount = poolAmounts[_token]; if (poolAmount == 0) { return 0; } uint256 _fundingRateFactor = stableTokens[_token] ? stableFundingRateFactor : fundingRateFactor; return _fundingRateFactor.mul(reservedAmounts[_token]).mul(intervals).div(poolAmount); } function getUtilisation(address _token) public view returns (uint256) { uint256 poolAmount = poolAmounts[_token]; if (poolAmount == 0) { return 0; } return reservedAmounts[_token].mul(FUNDING_RATE_PRECISION).div(poolAmount); } function getPositionLeverage(address _account, address _collateralToken, address _indexToken, bool _isLong) public view returns (uint256) { bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position memory position = positions[key]; _validate(position.collateral > 0, 37); return position.size.mul(BASIS_POINTS_DIVISOR).div(position.collateral); } // for longs: nextAveragePrice = (nextPrice * nextSize)/ (nextSize + delta) // for shorts: nextAveragePrice = (nextPrice * nextSize) / (nextSize - delta) function getNextAveragePrice(address _indexToken, uint256 _size, uint256 _averagePrice, bool _isLong, uint256 _nextPrice, uint256 _sizeDelta, uint256 _lastIncreasedTime) public view returns (uint256) { (bool hasProfit, uint256 delta) = getDelta(_indexToken, _size, _averagePrice, _isLong, _lastIncreasedTime); uint256 nextSize = _size.add(_sizeDelta); uint256 divisor; if (_isLong) { divisor = hasProfit ? nextSize.add(delta) : nextSize.sub(delta); } else { divisor = hasProfit ? nextSize.sub(delta) : nextSize.add(delta); } return _nextPrice.mul(nextSize).div(divisor); } // for longs: nextAveragePrice = (nextPrice * nextSize)/ (nextSize + delta) // for shorts: nextAveragePrice = (nextPrice * nextSize) / (nextSize - delta) function getNextGlobalShortAveragePrice(address _indexToken, uint256 _nextPrice, uint256 _sizeDelta) public view returns (uint256) { uint256 size = globalShortSizes[_indexToken]; uint256 averagePrice = globalShortAveragePrices[_indexToken]; uint256 priceDelta = averagePrice > _nextPrice ? averagePrice.sub(_nextPrice) : _nextPrice.sub(averagePrice); uint256 delta = size.mul(priceDelta).div(averagePrice); bool hasProfit = averagePrice > _nextPrice; uint256 nextSize = size.add(_sizeDelta); uint256 divisor = hasProfit ? nextSize.sub(delta) : nextSize.add(delta); return _nextPrice.mul(nextSize).div(divisor); } function getGlobalShortDelta(address _token) public view returns (bool, uint256) { uint256 size = globalShortSizes[_token]; if (size == 0) { return (false, 0); } uint256 nextPrice = getMaxPrice(_token); uint256 averagePrice = globalShortAveragePrices[_token]; uint256 priceDelta = averagePrice > nextPrice ? averagePrice.sub(nextPrice) : nextPrice.sub(averagePrice); uint256 delta = size.mul(priceDelta).div(averagePrice); bool hasProfit = averagePrice > nextPrice; return (hasProfit, delta); } function getPositionDelta(address _account, address _collateralToken, address _indexToken, bool _isLong) public view returns (bool, uint256) { bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position memory position = positions[key]; return getDelta(_indexToken, position.size, position.averagePrice, _isLong, position.lastIncreasedTime); } function getDelta(address _indexToken, uint256 _size, uint256 _averagePrice, bool _isLong, uint256 _lastIncreasedTime) public override view returns (bool, uint256) { _validate(_averagePrice > 0, 38); uint256 price = _isLong ? getMinPrice(_indexToken) : getMaxPrice(_indexToken); uint256 priceDelta = _averagePrice > price ? _averagePrice.sub(price) : price.sub(_averagePrice); uint256 delta = _size.mul(priceDelta).div(_averagePrice); bool hasProfit; if (_isLong) { hasProfit = price > _averagePrice; } else { hasProfit = _averagePrice > price; } // if the minProfitTime has passed then there will be no min profit threshold // the min profit threshold helps to prevent front-running issues uint256 minBps = block.timestamp > _lastIncreasedTime.add(minProfitTime) ? 0 : minProfitBasisPoints[_indexToken]; if (hasProfit && delta.mul(BASIS_POINTS_DIVISOR) <= _size.mul(minBps)) { delta = 0; } return (hasProfit, delta); } function getEntryFundingRate(address _collateralToken, address _indexToken, bool _isLong) public view returns (uint256) { return vaultUtils.getEntryFundingRate(_collateralToken, _indexToken, _isLong); } function getFundingFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _size, uint256 _entryFundingRate) public view returns (uint256) { return vaultUtils.getFundingFee(_account, _collateralToken, _indexToken, _isLong, _size, _entryFundingRate); } function getPositionFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta) public view returns (uint256) { return vaultUtils.getPositionFee(_account, _collateralToken, _indexToken, _isLong, _sizeDelta); } // cases to consider // 1. initialAmount is far from targetAmount, action increases balance slightly => high rebate // 2. initialAmount is far from targetAmount, action increases balance largely => high rebate // 3. initialAmount is close to targetAmount, action increases balance slightly => low rebate // 4. initialAmount is far from targetAmount, action reduces balance slightly => high tax // 5. initialAmount is far from targetAmount, action reduces balance largely => high tax // 6. initialAmount is close to targetAmount, action reduces balance largely => low tax // 7. initialAmount is above targetAmount, nextAmount is below targetAmount and vice versa // 8. a large swap should have similar fees as the same trade split into multiple smaller swaps function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) public override view returns (uint256) { return vaultUtils.getFeeBasisPoints(_token, _usdgDelta, _feeBasisPoints, _taxBasisPoints, _increment); } function getTargetUsdgAmount(address _token) public override view returns (uint256) { uint256 supply = IERC20(usdg).totalSupply(); if (supply == 0) { return 0; } uint256 weight = tokenWeights[_token]; return weight.mul(supply).div(totalTokenWeights); } function _reduceCollateral(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong) private returns (uint256, uint256) { bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong); Position storage position = positions[key]; uint256 fee = _collectMarginFees(_account, _collateralToken, _indexToken, _isLong, _sizeDelta, position.size, position.entryFundingRate); bool hasProfit; uint256 adjustedDelta; // scope variables to avoid stack too deep errors { (bool _hasProfit, uint256 delta) = getDelta(_indexToken, position.size, position.averagePrice, _isLong, position.lastIncreasedTime); hasProfit = _hasProfit; // get the proportional change in pnl adjustedDelta = _sizeDelta.mul(delta).div(position.size); } uint256 usdOut; // transfer profits out if (hasProfit && adjustedDelta > 0) { usdOut = adjustedDelta; position.realisedPnl = position.realisedPnl + int256(adjustedDelta); // pay out realised profits from the pool amount for short positions if (!_isLong) { uint256 tokenAmount = usdToTokenMin(_collateralToken, adjustedDelta); _decreasePoolAmount(_collateralToken, tokenAmount); } } if (!hasProfit && adjustedDelta > 0) { position.collateral = position.collateral.sub(adjustedDelta); // transfer realised losses to the pool for short positions // realised losses for long positions are not transferred here as // _increasePoolAmount was already called in increasePosition for longs if (!_isLong) { uint256 tokenAmount = usdToTokenMin(_collateralToken, adjustedDelta); _increasePoolAmount(_collateralToken, tokenAmount); } position.realisedPnl = position.realisedPnl - int256(adjustedDelta); } // reduce the position's collateral by _collateralDelta // transfer _collateralDelta out if (_collateralDelta > 0) { usdOut = usdOut.add(_collateralDelta); position.collateral = position.collateral.sub(_collateralDelta); } // if the position will be closed, then transfer the remaining collateral out if (position.size == _sizeDelta) { usdOut = usdOut.add(position.collateral); position.collateral = 0; } // if the usdOut is more than the fee then deduct the fee from the usdOut directly // else deduct the fee from the position's collateral uint256 usdOutAfterFee = usdOut; if (usdOut > fee) { usdOutAfterFee = usdOut.sub(fee); } else { position.collateral = position.collateral.sub(fee); if (_isLong) { uint256 feeTokens = usdToTokenMin(_collateralToken, fee); _decreasePoolAmount(_collateralToken, feeTokens); } } emit UpdatePnl(key, hasProfit, adjustedDelta); return (usdOut, usdOutAfterFee); } function _validatePosition(uint256 _size, uint256 _collateral) private view { if (_size == 0) { _validate(_collateral == 0, 39); return; } _validate(_size >= _collateral, 40); } function _validateRouter(address _account) private view { if (msg.sender == _account) { return; } if (msg.sender == router) { return; } _validate(approvedRouters[_account][msg.sender], 41); } function _validateTokens(address _collateralToken, address _indexToken, bool _isLong) private view { if (_isLong) { _validate(_collateralToken == _indexToken, 42); _validate(whitelistedTokens[_collateralToken], 43); _validate(!stableTokens[_collateralToken], 44); return; } _validate(whitelistedTokens[_collateralToken], 45); _validate(stableTokens[_collateralToken], 46); _validate(!stableTokens[_indexToken], 47); _validate(shortableTokens[_indexToken], 48); } function _collectSwapFees(address _token, uint256 _amount, uint256 _feeBasisPoints) private returns (uint256) { uint256 afterFeeAmount = _amount.mul(BASIS_POINTS_DIVISOR.sub(_feeBasisPoints)).div(BASIS_POINTS_DIVISOR); uint256 feeAmount = _amount.sub(afterFeeAmount); feeReserves[_token] = feeReserves[_token].add(feeAmount); emit CollectSwapFees(_token, tokenToUsdMin(_token, feeAmount), feeAmount); return afterFeeAmount; } function _collectMarginFees(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta, uint256 _size, uint256 _entryFundingRate) private returns (uint256) { uint256 feeUsd = getPositionFee(_account, _collateralToken, _indexToken, _isLong, _sizeDelta); uint256 fundingFee = getFundingFee(_account, _collateralToken, _indexToken, _isLong, _size, _entryFundingRate); feeUsd = feeUsd.add(fundingFee); uint256 feeTokens = usdToTokenMin(_collateralToken, feeUsd); feeReserves[_collateralToken] = feeReserves[_collateralToken].add(feeTokens); emit CollectMarginFees(_collateralToken, feeUsd, feeTokens); return feeUsd; } function _transferIn(address _token) private returns (uint256) { uint256 prevBalance = tokenBalances[_token]; uint256 nextBalance = IERC20(_token).balanceOf(address(this)); tokenBalances[_token] = nextBalance; return nextBalance.sub(prevBalance); } function _transferOut(address _token, uint256 _amount, address _receiver) private { IERC20(_token).safeTransfer(_receiver, _amount); tokenBalances[_token] = IERC20(_token).balanceOf(address(this)); } function _updateTokenBalance(address _token) private { uint256 nextBalance = IERC20(_token).balanceOf(address(this)); tokenBalances[_token] = nextBalance; } function _increasePoolAmount(address _token, uint256 _amount) private { poolAmounts[_token] = poolAmounts[_token].add(_amount); uint256 balance = IERC20(_token).balanceOf(address(this)); _validate(poolAmounts[_token] <= balance, 49); emit IncreasePoolAmount(_token, _amount); } function _decreasePoolAmount(address _token, uint256 _amount) private { poolAmounts[_token] = poolAmounts[_token].sub(_amount, "Vault: poolAmount exceeded"); _validate(reservedAmounts[_token] <= poolAmounts[_token], 50); emit DecreasePoolAmount(_token, _amount); } function _validateBufferAmount(address _token) private view { if (poolAmounts[_token] < bufferAmounts[_token]) { revert("Vault: poolAmount < buffer"); } } function _increaseUsdgAmount(address _token, uint256 _amount) private { usdgAmounts[_token] = usdgAmounts[_token].add(_amount); uint256 maxUsdgAmount = maxUsdgAmounts[_token]; if (maxUsdgAmount != 0) { _validate(usdgAmounts[_token] <= maxUsdgAmount, 51); } emit IncreaseUsdgAmount(_token, _amount); } function _decreaseUsdgAmount(address _token, uint256 _amount) private { uint256 value = usdgAmounts[_token]; // since USDG can be minted using multiple assets // it is possible for the USDG debt for a single asset to be less than zero // the USDG debt is capped to zero for this case if (value <= _amount) { usdgAmounts[_token] = 0; emit DecreaseUsdgAmount(_token, value); return; } usdgAmounts[_token] = value.sub(_amount); emit DecreaseUsdgAmount(_token, _amount); } function _increaseReservedAmount(address _token, uint256 _amount) private { reservedAmounts[_token] = reservedAmounts[_token].add(_amount); _validate(reservedAmounts[_token] <= poolAmounts[_token], 52); emit IncreaseReservedAmount(_token, _amount); } function _decreaseReservedAmount(address _token, uint256 _amount) private { reservedAmounts[_token] = reservedAmounts[_token].sub(_amount, "Vault: insufficient reserve"); emit DecreaseReservedAmount(_token, _amount); } function _increaseGuaranteedUsd(address _token, uint256 _usdAmount) private { guaranteedUsd[_token] = guaranteedUsd[_token].add(_usdAmount); emit IncreaseGuaranteedUsd(_token, _usdAmount); } function _decreaseGuaranteedUsd(address _token, uint256 _usdAmount) private { guaranteedUsd[_token] = guaranteedUsd[_token].sub(_usdAmount); emit DecreaseGuaranteedUsd(_token, _usdAmount); } function _increaseGlobalShortSize(address _token, uint256 _amount) internal { globalShortSizes[_token] = globalShortSizes[_token].add(_amount); uint256 maxSize = maxGlobalShortSizes[_token]; if (maxSize != 0) { require(globalShortSizes[_token] <= maxSize, "Vault: max shorts exceeded"); } } function _decreaseGlobalShortSize(address _token, uint256 _amount) private { uint256 size = globalShortSizes[_token]; if (_amount > size) { globalShortSizes[_token] = 0; return; } globalShortSizes[_token] = size.sub(_amount); } // we have this validation as a function instead of a modifier to reduce contract size function _onlyGov() private view { _validate(msg.sender == gov, 53); } // we have this validation as a function instead of a modifier to reduce contract size function _validateManager() private view { if (inManagerMode) { _validate(isManager[msg.sender], 54); } } // we have this validation as a function instead of a modifier to reduce contract size function _validateGasPrice() private view { if (maxGasPrice == 0) { return; } _validate(tx.gasprice <= maxGasPrice, 55); } function _validate(bool _condition, uint256 _errorCode) private view { require(_condition, errors[_errorCode]); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; contract Governable { address public gov; constructor() public { gov = msg.sender; } modifier onlyGov() { require(msg.sender == gov, "Governable: forbidden"); _; } function setGov(address _gov) external onlyGov { gov = _gov; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../access/Governable.sol"; contract Bridge is ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; address public token; address public wToken; constructor(address _token, address _wToken) public { token = _token; wToken = _wToken; } function wrap(uint256 _amount, address _receiver) external nonReentrant { IERC20(token).safeTransferFrom(msg.sender, address(this), _amount); IERC20(wToken).safeTransfer(_receiver, _amount); } function unwrap(uint256 _amount, address _receiver) external nonReentrant { IERC20(wToken).safeTransferFrom(msg.sender, address(this), _amount); IERC20(token).safeTransfer(_receiver, _amount); } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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 `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool); /** * @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); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./IERC20.sol"; import "../math/SafeMath.sol"; import "../utils/Address.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 SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @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(IERC20 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' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @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(IERC20 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 // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /** * @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]. */ contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor () internal { _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 make 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; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.2; /** * @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 * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 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"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (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"); // solhint-disable-next-line avoid-low-level-calls (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"); // solhint-disable-next-line avoid-low-level-calls (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.3._ */ 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.3._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(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 _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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 // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IDistributor.sol"; import "./interfaces/IYieldTracker.sol"; import "./interfaces/IYieldToken.sol"; // code adapated from https://github.com/trusttoken/smart-contracts/blob/master/contracts/truefi/TrueFarm.sol contract YieldTracker is IYieldTracker, ReentrancyGuard { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant PRECISION = 1e30; address public gov; address public yieldToken; address public distributor; uint256 public cumulativeRewardPerToken; mapping (address => uint256) public claimableReward; mapping (address => uint256) public previousCumulatedRewardPerToken; event Claim(address receiver, uint256 amount); modifier onlyGov() { require(msg.sender == gov, "YieldTracker: forbidden"); _; } constructor(address _yieldToken) public { gov = msg.sender; yieldToken = _yieldToken; } function setGov(address _gov) external onlyGov { gov = _gov; } function setDistributor(address _distributor) external onlyGov { distributor = _distributor; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function claim(address _account, address _receiver) external override returns (uint256) { require(msg.sender == yieldToken, "YieldTracker: forbidden"); updateRewards(_account); uint256 tokenAmount = claimableReward[_account]; claimableReward[_account] = 0; address rewardToken = IDistributor(distributor).getRewardToken(address(this)); IERC20(rewardToken).safeTransfer(_receiver, tokenAmount); emit Claim(_account, tokenAmount); return tokenAmount; } function getTokensPerInterval() external override view returns (uint256) { return IDistributor(distributor).tokensPerInterval(address(this)); } function claimable(address _account) external override view returns (uint256) { uint256 stakedBalance = IYieldToken(yieldToken).stakedBalance(_account); if (stakedBalance == 0) { return claimableReward[_account]; } uint256 pendingRewards = IDistributor(distributor).getDistributionAmount(address(this)).mul(PRECISION); uint256 totalStaked = IYieldToken(yieldToken).totalStaked(); uint256 nextCumulativeRewardPerToken = cumulativeRewardPerToken.add(pendingRewards.div(totalStaked)); return claimableReward[_account].add( stakedBalance.mul(nextCumulativeRewardPerToken.sub(previousCumulatedRewardPerToken[_account])).div(PRECISION)); } function updateRewards(address _account) public override nonReentrant { uint256 blockReward; if (distributor != address(0)) { blockReward = IDistributor(distributor).distribute(); } uint256 _cumulativeRewardPerToken = cumulativeRewardPerToken; uint256 totalStaked = IYieldToken(yieldToken).totalStaked(); // only update cumulativeRewardPerToken when there are stakers, i.e. when totalStaked > 0 // if blockReward == 0, then there will be no change to cumulativeRewardPerToken if (totalStaked > 0 && blockReward > 0) { _cumulativeRewardPerToken = _cumulativeRewardPerToken.add(blockReward.mul(PRECISION).div(totalStaked)); cumulativeRewardPerToken = _cumulativeRewardPerToken; } // cumulativeRewardPerToken can only increase // so if cumulativeRewardPerToken is zero, it means there are no rewards yet if (_cumulativeRewardPerToken == 0) { return; } if (_account != address(0)) { uint256 stakedBalance = IYieldToken(yieldToken).stakedBalance(_account); uint256 _previousCumulatedReward = previousCumulatedRewardPerToken[_account]; uint256 _claimableReward = claimableReward[_account].add( stakedBalance.mul(_cumulativeRewardPerToken.sub(_previousCumulatedReward)).div(PRECISION) ); claimableReward[_account] = _claimableReward; previousCumulatedRewardPerToken[_account] = _cumulativeRewardPerToken; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IDistributor { function distribute() external returns (uint256); function getRewardToken(address _receiver) external view returns (address); function getDistributionAmount(address _receiver) external view returns (uint256); function tokensPerInterval(address _receiver) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IYieldTracker { function claim(address _account, address _receiver) external returns (uint256); function updateRewards(address _account) external; function getTokensPerInterval() external view returns (uint256); function claimable(address _account) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IYieldToken { function totalStaked() external view returns (uint256); function stakedBalance(address _account) external view returns (uint256); function removeAdmin(address _account) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "./interfaces/IYieldTracker.sol"; import "./interfaces/IYieldToken.sol"; contract YieldToken is IERC20, IYieldToken { using SafeMath for uint256; using SafeERC20 for IERC20; string public name; string public symbol; uint8 public constant decimals = 18; uint256 public override totalSupply; uint256 public nonStakingSupply; address public gov; mapping (address => uint256) public balances; mapping (address => mapping (address => uint256)) public allowances; address[] public yieldTrackers; mapping (address => bool) public nonStakingAccounts; mapping (address => bool) public admins; bool public inWhitelistMode; mapping (address => bool) public whitelistedHandlers; modifier onlyGov() { require(msg.sender == gov, "YieldToken: forbidden"); _; } modifier onlyAdmin() { require(admins[msg.sender], "YieldToken: forbidden"); _; } constructor(string memory _name, string memory _symbol, uint256 _initialSupply) public { name = _name; symbol = _symbol; gov = msg.sender; admins[msg.sender] = true; _mint(msg.sender, _initialSupply); } function setGov(address _gov) external onlyGov { gov = _gov; } function setInfo(string memory _name, string memory _symbol) external onlyGov { name = _name; symbol = _symbol; } function setYieldTrackers(address[] memory _yieldTrackers) external onlyGov { yieldTrackers = _yieldTrackers; } function addAdmin(address _account) external onlyGov { admins[_account] = true; } function removeAdmin(address _account) external override onlyGov { admins[_account] = false; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function setInWhitelistMode(bool _inWhitelistMode) external onlyGov { inWhitelistMode = _inWhitelistMode; } function setWhitelistedHandler(address _handler, bool _isWhitelisted) external onlyGov { whitelistedHandlers[_handler] = _isWhitelisted; } function addNonStakingAccount(address _account) external onlyAdmin { require(!nonStakingAccounts[_account], "YieldToken: _account already marked"); _updateRewards(_account); nonStakingAccounts[_account] = true; nonStakingSupply = nonStakingSupply.add(balances[_account]); } function removeNonStakingAccount(address _account) external onlyAdmin { require(nonStakingAccounts[_account], "YieldToken: _account not marked"); _updateRewards(_account); nonStakingAccounts[_account] = false; nonStakingSupply = nonStakingSupply.sub(balances[_account]); } function recoverClaim(address _account, address _receiver) external onlyAdmin { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).claim(_account, _receiver); } } function claim(address _receiver) external { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).claim(msg.sender, _receiver); } } function totalStaked() external view override returns (uint256) { return totalSupply.sub(nonStakingSupply); } function balanceOf(address _account) external view override returns (uint256) { return balances[_account]; } function stakedBalance(address _account) external view override returns (uint256) { if (nonStakingAccounts[_account]) { return 0; } return balances[_account]; } function transfer(address _recipient, uint256 _amount) external override returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function allowance(address _owner, address _spender) external view override returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external override returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) { uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "YieldToken: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function _mint(address _account, uint256 _amount) internal { require(_account != address(0), "YieldToken: mint to the zero address"); _updateRewards(_account); totalSupply = totalSupply.add(_amount); balances[_account] = balances[_account].add(_amount); if (nonStakingAccounts[_account]) { nonStakingSupply = nonStakingSupply.add(_amount); } emit Transfer(address(0), _account, _amount); } function _burn(address _account, uint256 _amount) internal { require(_account != address(0), "YieldToken: burn from the zero address"); _updateRewards(_account); balances[_account] = balances[_account].sub(_amount, "YieldToken: burn amount exceeds balance"); totalSupply = totalSupply.sub(_amount); if (nonStakingAccounts[_account]) { nonStakingSupply = nonStakingSupply.sub(_amount); } emit Transfer(_account, address(0), _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "YieldToken: transfer from the zero address"); require(_recipient != address(0), "YieldToken: transfer to the zero address"); if (inWhitelistMode) { require(whitelistedHandlers[msg.sender], "YieldToken: msg.sender not whitelisted"); } _updateRewards(_sender); _updateRewards(_recipient); balances[_sender] = balances[_sender].sub(_amount, "YieldToken: transfer amount exceeds balance"); balances[_recipient] = balances[_recipient].add(_amount); if (nonStakingAccounts[_sender]) { nonStakingSupply = nonStakingSupply.sub(_amount); } if (nonStakingAccounts[_recipient]) { nonStakingSupply = nonStakingSupply.add(_amount); } emit Transfer(_sender, _recipient,_amount); } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "YieldToken: approve from the zero address"); require(_spender != address(0), "YieldToken: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } function _updateRewards(address _account) private { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).updateRewards(_account); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IYieldTracker.sol"; import "./interfaces/IBaseToken.sol"; import "./interfaces/IUSDG.sol"; import "./interfaces/IExtendedBaseToken.sol"; import "./interfaces/IExtendedYieldTracker.sol"; import "./interfaces/IExtendedUSDG.sol"; import "hardhat/console.sol"; contract TestYieldTracker { // test using avax addresses address glpAddress = 0x01234181085565ed162a948b6a5e88758CD7c7b8; address gmxAddress = 0x62edc0692BD897D2295872a9FFCac5425011c661; address usdgAddress = 0xc0253c3cC6aa5Ab407b5795a04c28fB063273894; //test again glp (base token), gmx(base token) and usdg(yield token) function getYieldTrackersForGLP() external returns (address[] memory) { address[] memory yieldTrackers = IExtendedBaseToken(glpAddress).getYieldTrackers(); for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; console.log('yield tracker for glp '); console.log(i); console.log(yieldTracker); address yieldToken = IExtendedYieldTracker(yieldTracker).getYieldToken(); console.log("yield token "); console.log(yieldToken); } return yieldTrackers; } function getYieldTrackersForGMX() external returns (address[] memory) { address[] memory yieldTrackers = IExtendedBaseToken(gmxAddress).getYieldTrackers(); for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; console.log('yield tracker for gmx '); console.log(i); console.log(yieldTracker); address yieldToken = IExtendedYieldTracker(yieldTracker).getYieldToken(); console.log("yield token "); console.log(yieldToken); } return yieldTrackers; } function getYieldTrackersForUSDG() external returns (address[] memory){ address[] memory yieldTrackers = IExtendedUSDG(usdgAddress).getYieldTrackers(); for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; console.log('yield tracker for usdg '); console.log(i); console.log(yieldTracker); address yieldToken = IExtendedYieldTracker(yieldTracker).getYieldToken(); console.log("yield token "); console.log(yieldToken); } return yieldTrackers; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IBaseToken { function totalStaked() external view returns (uint256); function stakedBalance(address _account) external view returns (uint256); function removeAdmin(address _account) external; function setInPrivateTransferMode(bool _inPrivateTransferMode) external; function withdrawToken(address _token, address _account, uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IUSDG { function addVault(address _vault) external; function removeVault(address _vault) external; function mint(address _account, uint256 _amount) external; function burn(address _account, uint256 _amount) external; }
// SPDX-License-Identifier: MIT import "./IBaseToken.sol"; interface IExtendedBaseToken is IBaseToken { function getYieldTrackers() external view returns (address[] memory); }
// SPDX-License-Identifier: MIT import "./IYieldTracker.sol"; interface IExtendedYieldTracker is IYieldTracker { function getYieldToken() external view returns (address); }
// SPDX-License-Identifier: MIT import "./IUSDG.sol"; interface IExtendedUSDG is IUSDG { function getYieldTrackers() external view returns (address[] memory); }
// SPDX-License-Identifier: MIT pragma solidity >= 0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); function _sendLogPayload(bytes memory payload) private view { uint256 payloadLength = payload.length; address consoleAddress = CONSOLE_ADDRESS; assembly { let payloadStart := add(payload, 32) let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) } } function log() internal view { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int256 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } function logUint(uint256 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function logString(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint256 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function log(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint256 p0, uint256 p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); } function log(uint256 p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); } function log(uint256 p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); } function log(uint256 p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); } function log(string memory p0, uint256 p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); } function log(string memory p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint256 p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); } function log(bool p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint256 p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); } function log(address p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint256 p0, uint256 p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); } function log(uint256 p0, string memory p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); } function log(uint256 p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); } function log(uint256 p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); } function log(uint256 p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); } function log(uint256 p0, bool p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); } function log(uint256 p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); } function log(uint256 p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); } function log(uint256 p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); } function log(uint256 p0, address p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); } function log(uint256 p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); } function log(uint256 p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); } function log(uint256 p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); } function log(string memory p0, uint256 p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); } function log(string memory p0, uint256 p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); } function log(string memory p0, uint256 p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); } function log(string memory p0, uint256 p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint256 p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); } function log(bool p0, uint256 p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); } function log(bool p0, uint256 p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); } function log(bool p0, uint256 p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint256 p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); } function log(address p0, uint256 p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); } function log(address p0, uint256 p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); } function log(address p0, uint256 p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint256 p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint256 p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: MIT import "hardhat/console.sol"; pragma solidity ^0.6.0; contract MaliciousTraderTest { event Received(); address public positionRouter; constructor(address _positionRouter) public { positionRouter = _positionRouter; } receive() external payable { // just consume lot of gas uint256 a; for (uint i = 0; i < 1000000; i++) { a = a * i; } emit Received(); } function createIncreasePositionETH( address[] memory _path, address _indexToken, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee, bytes32 _referralCode, address _callbackTarget ) external payable { console.log("path.length %s indexToken %s minOut %s", _path.length, _indexToken, _minOut); console.log("sizeDelta %s isLong %s acceptablePrice %s", _sizeDelta, _isLong, _acceptablePrice); console.log("executionFee %s callbackTarget %s", _executionFee, _callbackTarget); (bool success, bytes memory reason) = positionRouter.call{value: msg.value}( abi.encodeWithSignature( "createIncreasePositionETH(address[],address,uint256,uint256,bool,uint256,uint256,bytes32,address)", _path, _indexToken, _minOut, _sizeDelta, _isLong, _acceptablePrice, _executionFee, _referralCode, _callbackTarget ) ); console.log("success: %s reason: '%s'", success, string(reason)); require(success, string(reason)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IUSDG.sol"; import "./YieldToken.sol"; contract USDG is YieldToken, IUSDG { mapping (address => bool) public vaults; modifier onlyVault() { require(vaults[msg.sender], "USDG: forbidden"); _; } constructor(address _vault) public YieldToken("USD Gambit", "USDG", 0) { vaults[_vault] = true; } function addVault(address _vault) external override onlyGov { vaults[_vault] = true; } function removeVault(address _vault) external override onlyGov { vaults[_vault] = false; } function mint(address _account, uint256 _amount) external override onlyVault { _mint(_account, _amount); } function burn(address _account, uint256 _amount) external override onlyVault { _burn(_account, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/ITimelockTarget.sol"; import "./interfaces/ITimelock.sol"; import "./interfaces/IHandlerTarget.sol"; import "../access/interfaces/IAdmin.sol"; import "../core/interfaces/IVault.sol"; import "../core/interfaces/IVaultUtils.sol"; import "../core/interfaces/IGlpManager.sol"; import "../referrals/interfaces/IReferralStorage.sol"; import "../tokens/interfaces/IYieldToken.sol"; import "../tokens/interfaces/IBaseToken.sol"; import "../tokens/interfaces/IMintable.sol"; import "../tokens/interfaces/IUSDG.sol"; import "../staking/interfaces/IVester.sol"; import "../staking/interfaces/IRewardRouterV2.sol"; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; contract Timelock is ITimelock { using SafeMath for uint256; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant MAX_BUFFER = 5 days; uint256 public constant MAX_FUNDING_RATE_FACTOR = 200; // 0.02% uint256 public constant MAX_LEVERAGE_VALIDATION = 500000; // 50x uint256 public buffer; address public admin; address public tokenManager; address public mintReceiver; address public glpManager; address public rewardRouter; uint256 public maxTokenSupply; uint256 public override marginFeeBasisPoints; uint256 public maxMarginFeeBasisPoints; bool public shouldToggleIsLeverageEnabled; mapping (bytes32 => uint256) public pendingActions; mapping (address => bool) public isHandler; mapping (address => bool) public isKeeper; event SignalPendingAction(bytes32 action); event SignalApprove(address token, address spender, uint256 amount, bytes32 action); event SignalWithdrawToken(address target, address token, address receiver, uint256 amount, bytes32 action); event SignalMint(address token, address receiver, uint256 amount, bytes32 action); event SignalSetGov(address target, address gov, bytes32 action); event SignalSetHandler(address target, address handler, bool isActive, bytes32 action); event SignalSetPriceFeed(address vault, address priceFeed, bytes32 action); event SignalRedeemUsdg(address vault, address token, uint256 amount); event SignalVaultSetTokenConfig( address vault, address token, uint256 tokenDecimals, uint256 tokenWeight, uint256 minProfitBps, uint256 maxUsdgAmount, bool isStable, bool isShortable ); event ClearAction(bytes32 action); modifier onlyAdmin() { require(msg.sender == admin, "Timelock: forbidden"); _; } modifier onlyHandlerAndAbove() { require(msg.sender == admin || isHandler[msg.sender], "Timelock: forbidden"); _; } modifier onlyKeeperAndAbove() { require(msg.sender == admin || isHandler[msg.sender] || isKeeper[msg.sender], "Timelock: forbidden"); _; } modifier onlyTokenManager() { require(msg.sender == tokenManager, "Timelock: forbidden"); _; } constructor( address _admin, uint256 _buffer, address _tokenManager, address _mintReceiver, address _glpManager, address _rewardRouter, uint256 _maxTokenSupply, uint256 _marginFeeBasisPoints, uint256 _maxMarginFeeBasisPoints ) public { require(_buffer <= MAX_BUFFER, "Timelock: invalid _buffer"); admin = _admin; buffer = _buffer; tokenManager = _tokenManager; mintReceiver = _mintReceiver; glpManager = _glpManager; rewardRouter = _rewardRouter; maxTokenSupply = _maxTokenSupply; marginFeeBasisPoints = _marginFeeBasisPoints; maxMarginFeeBasisPoints = _maxMarginFeeBasisPoints; } function setAdmin(address _admin) external override onlyTokenManager { admin = _admin; } function setExternalAdmin(address _target, address _admin) external onlyAdmin { require(_target != address(this), "Timelock: invalid _target"); IAdmin(_target).setAdmin(_admin); } function setContractHandler(address _handler, bool _isActive) external onlyAdmin { isHandler[_handler] = _isActive; } function initGlpManager() external onlyAdmin { IGlpManager _glpManager = IGlpManager(glpManager); IMintable glp = IMintable(_glpManager.glp()); glp.setMinter(glpManager, true); IUSDG usdg = IUSDG(_glpManager.usdg()); usdg.addVault(glpManager); IVault vault = _glpManager.vault(); vault.setManager(glpManager, true); } function initRewardRouter() external onlyAdmin { IRewardRouterV2 _rewardRouter = IRewardRouterV2(rewardRouter); IHandlerTarget(_rewardRouter.feeGlpTracker()).setHandler(rewardRouter, true); IHandlerTarget(_rewardRouter.stakedGlpTracker()).setHandler(rewardRouter, true); IHandlerTarget(glpManager).setHandler(rewardRouter, true); } function setKeeper(address _keeper, bool _isActive) external onlyAdmin { isKeeper[_keeper] = _isActive; } function setBuffer(uint256 _buffer) external onlyAdmin { require(_buffer <= MAX_BUFFER, "Timelock: invalid _buffer"); require(_buffer > buffer, "Timelock: buffer cannot be decreased"); buffer = _buffer; } function setMaxLeverage(address _vault, uint256 _maxLeverage) external onlyAdmin { require(_maxLeverage > MAX_LEVERAGE_VALIDATION, "Timelock: invalid _maxLeverage"); IVault(_vault).setMaxLeverage(_maxLeverage); } function setFundingRate(address _vault, uint256 _fundingInterval, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor) external onlyKeeperAndAbove { require(_fundingRateFactor < MAX_FUNDING_RATE_FACTOR, "Timelock: invalid _fundingRateFactor"); require(_stableFundingRateFactor < MAX_FUNDING_RATE_FACTOR, "Timelock: invalid _stableFundingRateFactor"); IVault(_vault).setFundingRate(_fundingInterval, _fundingRateFactor, _stableFundingRateFactor); } function setShouldToggleIsLeverageEnabled(bool _shouldToggleIsLeverageEnabled) external onlyHandlerAndAbove { shouldToggleIsLeverageEnabled = _shouldToggleIsLeverageEnabled; } function setMarginFeeBasisPoints(uint256 _marginFeeBasisPoints, uint256 _maxMarginFeeBasisPoints) external onlyHandlerAndAbove { marginFeeBasisPoints = _marginFeeBasisPoints; maxMarginFeeBasisPoints = _maxMarginFeeBasisPoints; } function setSwapFees( address _vault, uint256 _taxBasisPoints, uint256 _stableTaxBasisPoints, uint256 _mintBurnFeeBasisPoints, uint256 _swapFeeBasisPoints, uint256 _stableSwapFeeBasisPoints ) external onlyKeeperAndAbove { IVault vault = IVault(_vault); vault.setFees( _taxBasisPoints, _stableTaxBasisPoints, _mintBurnFeeBasisPoints, _swapFeeBasisPoints, _stableSwapFeeBasisPoints, maxMarginFeeBasisPoints, vault.liquidationFeeUsd(), vault.minProfitTime(), vault.hasDynamicFees() ); } // assign _marginFeeBasisPoints to this.marginFeeBasisPoints // because enableLeverage would update Vault.marginFeeBasisPoints to this.marginFeeBasisPoints // and disableLeverage would reset the Vault.marginFeeBasisPoints to this.maxMarginFeeBasisPoints function setFees( address _vault, uint256 _taxBasisPoints, uint256 _stableTaxBasisPoints, uint256 _mintBurnFeeBasisPoints, uint256 _swapFeeBasisPoints, uint256 _stableSwapFeeBasisPoints, uint256 _marginFeeBasisPoints, uint256 _liquidationFeeUsd, uint256 _minProfitTime, bool _hasDynamicFees ) external onlyKeeperAndAbove { marginFeeBasisPoints = _marginFeeBasisPoints; IVault(_vault).setFees( _taxBasisPoints, _stableTaxBasisPoints, _mintBurnFeeBasisPoints, _swapFeeBasisPoints, _stableSwapFeeBasisPoints, maxMarginFeeBasisPoints, _liquidationFeeUsd, _minProfitTime, _hasDynamicFees ); } function enableLeverage(address _vault) external override onlyHandlerAndAbove { IVault vault = IVault(_vault); if (shouldToggleIsLeverageEnabled) { vault.setIsLeverageEnabled(true); } vault.setFees( vault.taxBasisPoints(), vault.stableTaxBasisPoints(), vault.mintBurnFeeBasisPoints(), vault.swapFeeBasisPoints(), vault.stableSwapFeeBasisPoints(), marginFeeBasisPoints, vault.liquidationFeeUsd(), vault.minProfitTime(), vault.hasDynamicFees() ); } function disableLeverage(address _vault) external override onlyHandlerAndAbove { IVault vault = IVault(_vault); if (shouldToggleIsLeverageEnabled) { vault.setIsLeverageEnabled(false); } vault.setFees( vault.taxBasisPoints(), vault.stableTaxBasisPoints(), vault.mintBurnFeeBasisPoints(), vault.swapFeeBasisPoints(), vault.stableSwapFeeBasisPoints(), maxMarginFeeBasisPoints, // marginFeeBasisPoints vault.liquidationFeeUsd(), vault.minProfitTime(), vault.hasDynamicFees() ); } function setIsLeverageEnabled(address _vault, bool _isLeverageEnabled) external override onlyHandlerAndAbove { IVault(_vault).setIsLeverageEnabled(_isLeverageEnabled); } function setTokenConfig( address _vault, address _token, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, uint256 _bufferAmount, uint256 _usdgAmount ) external onlyKeeperAndAbove { require(_minProfitBps <= 500, "Timelock: invalid _minProfitBps"); IVault vault = IVault(_vault); require(vault.whitelistedTokens(_token), "Timelock: token not yet whitelisted"); uint256 tokenDecimals = vault.tokenDecimals(_token); bool isStable = vault.stableTokens(_token); bool isShortable = vault.shortableTokens(_token); IVault(_vault).setTokenConfig( _token, tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, isStable, isShortable ); IVault(_vault).setBufferAmount(_token, _bufferAmount); IVault(_vault).setUsdgAmount(_token, _usdgAmount); } function setUsdgAmounts(address _vault, address[] memory _tokens, uint256[] memory _usdgAmounts) external onlyKeeperAndAbove { for (uint256 i = 0; i < _tokens.length; i++) { IVault(_vault).setUsdgAmount(_tokens[i], _usdgAmounts[i]); } } function updateUsdgSupply(uint256 usdgAmount) external onlyKeeperAndAbove { address usdg = IGlpManager(glpManager).usdg(); uint256 balance = IERC20(usdg).balanceOf(glpManager); IUSDG(usdg).addVault(address(this)); if (usdgAmount > balance) { uint256 mintAmount = usdgAmount.sub(balance); IUSDG(usdg).mint(glpManager, mintAmount); } else { uint256 burnAmount = balance.sub(usdgAmount); IUSDG(usdg).burn(glpManager, burnAmount); } IUSDG(usdg).removeVault(address(this)); } function setShortsTrackerAveragePriceWeight(uint256 _shortsTrackerAveragePriceWeight) external onlyAdmin { IGlpManager(glpManager).setShortsTrackerAveragePriceWeight(_shortsTrackerAveragePriceWeight); } function setGlpCooldownDuration(uint256 _cooldownDuration) external onlyAdmin { require(_cooldownDuration < 2 hours, "Timelock: invalid _cooldownDuration"); IGlpManager(glpManager).setCooldownDuration(_cooldownDuration); } function setMaxGlobalShortSize(address _vault, address _token, uint256 _amount) external onlyAdmin { IVault(_vault).setMaxGlobalShortSize(_token, _amount); } function removeAdmin(address _token, address _account) external onlyAdmin { IYieldToken(_token).removeAdmin(_account); } function setIsSwapEnabled(address _vault, bool _isSwapEnabled) external onlyKeeperAndAbove { IVault(_vault).setIsSwapEnabled(_isSwapEnabled); } function setTier(address _referralStorage, uint256 _tierId, uint256 _totalRebate, uint256 _discountShare) external onlyKeeperAndAbove { IReferralStorage(_referralStorage).setTier(_tierId, _totalRebate, _discountShare); } function setReferrerTier(address _referralStorage, address _referrer, uint256 _tierId) external onlyKeeperAndAbove { IReferralStorage(_referralStorage).setReferrerTier(_referrer, _tierId); } function govSetCodeOwner(address _referralStorage, bytes32 _code, address _newAccount) external onlyKeeperAndAbove { IReferralStorage(_referralStorage).govSetCodeOwner(_code, _newAccount); } function setVaultUtils(address _vault, IVaultUtils _vaultUtils) external onlyAdmin { IVault(_vault).setVaultUtils(_vaultUtils); } function setMaxGasPrice(address _vault, uint256 _maxGasPrice) external onlyAdmin { require(_maxGasPrice > 5000000000, "Invalid _maxGasPrice"); IVault(_vault).setMaxGasPrice(_maxGasPrice); } function withdrawFees(address _vault, address _token, address _receiver) external onlyAdmin { IVault(_vault).withdrawFees(_token, _receiver); } function batchWithdrawFees(address _vault, address[] memory _tokens) external onlyKeeperAndAbove { for (uint256 i = 0; i < _tokens.length; i++) { IVault(_vault).withdrawFees(_tokens[i], admin); } } function setInPrivateLiquidationMode(address _vault, bool _inPrivateLiquidationMode) external onlyAdmin { IVault(_vault).setInPrivateLiquidationMode(_inPrivateLiquidationMode); } function setLiquidator(address _vault, address _liquidator, bool _isActive) external onlyAdmin { IVault(_vault).setLiquidator(_liquidator, _isActive); } function setInPrivateTransferMode(address _token, bool _inPrivateTransferMode) external onlyAdmin { IBaseToken(_token).setInPrivateTransferMode(_inPrivateTransferMode); } function batchSetBonusRewards(address _vester, address[] memory _accounts, uint256[] memory _amounts) external onlyKeeperAndAbove { require(_accounts.length == _amounts.length, "Timelock: invalid lengths"); IHandlerTarget(_vester).setHandler(address(this), true); for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; uint256 amount = _amounts[i]; IVester(_vester).setBonusRewards(account, amount); } IHandlerTarget(_vester).setHandler(address(this), false); } function transferIn(address _sender, address _token, uint256 _amount) external onlyAdmin { IERC20(_token).transferFrom(_sender, address(this), _amount); } function signalApprove(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _setPendingAction(action); emit SignalApprove(_token, _spender, _amount, action); } function approve(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _validateAction(action); _clearAction(action); IERC20(_token).approve(_spender, _amount); } function signalWithdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _setPendingAction(action); emit SignalWithdrawToken(_target, _token, _receiver, _amount, action); } function withdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _validateAction(action); _clearAction(action); IBaseToken(_target).withdrawToken(_token, _receiver, _amount); } function signalMint(address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("mint", _token, _receiver, _amount)); _setPendingAction(action); emit SignalMint(_token, _receiver, _amount, action); } function processMint(address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("mint", _token, _receiver, _amount)); _validateAction(action); _clearAction(action); _mint(_token, _receiver, _amount); } function signalSetGov(address _target, address _gov) external override onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _setPendingAction(action); emit SignalSetGov(_target, _gov, action); } function setGov(address _target, address _gov) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _validateAction(action); _clearAction(action); ITimelockTarget(_target).setGov(_gov); } function signalSetHandler(address _target, address _handler, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive)); _setPendingAction(action); emit SignalSetHandler(_target, _handler, _isActive, action); } function setHandler(address _target, address _handler, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive)); _validateAction(action); _clearAction(action); IHandlerTarget(_target).setHandler(_handler, _isActive); } function signalSetPriceFeed(address _vault, address _priceFeed) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeed", _vault, _priceFeed)); _setPendingAction(action); emit SignalSetPriceFeed(_vault, _priceFeed, action); } function setPriceFeed(address _vault, address _priceFeed) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeed", _vault, _priceFeed)); _validateAction(action); _clearAction(action); IVault(_vault).setPriceFeed(_priceFeed); } function signalRedeemUsdg(address _vault, address _token, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("redeemUsdg", _vault, _token, _amount)); _setPendingAction(action); emit SignalRedeemUsdg(_vault, _token, _amount); } function redeemUsdg(address _vault, address _token, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("redeemUsdg", _vault, _token, _amount)); _validateAction(action); _clearAction(action); address usdg = IVault(_vault).usdg(); IVault(_vault).setManager(address(this), true); IUSDG(usdg).addVault(address(this)); IUSDG(usdg).mint(address(this), _amount); IERC20(usdg).transfer(address(_vault), _amount); IVault(_vault).sellUSDG(_token, mintReceiver); IVault(_vault).setManager(address(this), false); IUSDG(usdg).removeVault(address(this)); } function signalVaultSetTokenConfig( address _vault, address _token, uint256 _tokenDecimals, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "vaultSetTokenConfig", _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable )); _setPendingAction(action); emit SignalVaultSetTokenConfig( _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable ); } function vaultSetTokenConfig( address _vault, address _token, uint256 _tokenDecimals, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "vaultSetTokenConfig", _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable )); _validateAction(action); _clearAction(action); IVault(_vault).setTokenConfig( _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable ); } function cancelAction(bytes32 _action) external onlyAdmin { _clearAction(_action); } function _mint(address _token, address _receiver, uint256 _amount) private { IMintable mintable = IMintable(_token); mintable.setMinter(address(this), true); mintable.mint(_receiver, _amount); require(IERC20(_token).totalSupply() <= maxTokenSupply, "Timelock: maxTokenSupply exceeded"); mintable.setMinter(address(this), false); } function _setPendingAction(bytes32 _action) private { require(pendingActions[_action] == 0, "Timelock: action already signalled"); pendingActions[_action] = block.timestamp.add(buffer); emit SignalPendingAction(_action); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action] != 0, "Timelock: action not signalled"); require(pendingActions[_action] < block.timestamp, "Timelock: action time not yet passed"); } function _clearAction(bytes32 _action) private { require(pendingActions[_action] != 0, "Timelock: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface ITimelockTarget { function setGov(address _gov) external; function withdrawToken(address _token, address _account, uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface ITimelock { function marginFeeBasisPoints() external returns (uint256); function setAdmin(address _admin) external; function enableLeverage(address _vault) external; function disableLeverage(address _vault) external; function setIsLeverageEnabled(address _vault, bool _isLeverageEnabled) external; function signalSetGov(address _target, address _gov) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IHandlerTarget { function isHandler(address _account) external returns (bool); function setHandler(address _handler, bool _isActive) external; }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAdmin { function setAdmin(address _admin) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./IVaultUtils.sol"; interface IVault { function isInitialized() external view returns (bool); function isSwapEnabled() external view returns (bool); function isLeverageEnabled() external view returns (bool); function setVaultUtils(IVaultUtils _vaultUtils) external; function setError(uint256 _errorCode, string calldata _error) external; function router() external view returns (address); function usdg() external view returns (address); function gov() external view returns (address); function whitelistedTokenCount() external view returns (uint256); function maxLeverage() external view returns (uint256); function minProfitTime() external view returns (uint256); function hasDynamicFees() external view returns (bool); function fundingInterval() external view returns (uint256); function totalTokenWeights() external view returns (uint256); function getTargetUsdgAmount(address _token) external view returns (uint256); function inManagerMode() external view returns (bool); function inPrivateLiquidationMode() external view returns (bool); function maxGasPrice() external view returns (uint256); function approvedRouters(address _account, address _router) external view returns (bool); function isLiquidator(address _account) external view returns (bool); function isManager(address _account) external view returns (bool); function minProfitBasisPoints(address _token) external view returns (uint256); function tokenBalances(address _token) external view returns (uint256); function lastFundingTimes(address _token) external view returns (uint256); function setMaxLeverage(uint256 _maxLeverage) external; function setInManagerMode(bool _inManagerMode) external; function setManager(address _manager, bool _isManager) external; function setIsSwapEnabled(bool _isSwapEnabled) external; function setIsLeverageEnabled(bool _isLeverageEnabled) external; function setMaxGasPrice(uint256 _maxGasPrice) external; function setUsdgAmount(address _token, uint256 _amount) external; function setBufferAmount(address _token, uint256 _amount) external; function setMaxGlobalShortSize(address _token, uint256 _amount) external; function setInPrivateLiquidationMode(bool _inPrivateLiquidationMode) external; function setLiquidator(address _liquidator, bool _isActive) external; function setFundingRate(uint256 _fundingInterval, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor) external; function setFees( uint256 _taxBasisPoints, uint256 _stableTaxBasisPoints, uint256 _mintBurnFeeBasisPoints, uint256 _swapFeeBasisPoints, uint256 _stableSwapFeeBasisPoints, uint256 _marginFeeBasisPoints, uint256 _liquidationFeeUsd, uint256 _minProfitTime, bool _hasDynamicFees ) external; function setTokenConfig( address _token, uint256 _tokenDecimals, uint256 _redemptionBps, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external; function setPriceFeed(address _priceFeed) external; function withdrawFees(address _token, address _receiver) external returns (uint256); function directPoolDeposit(address _token) external; function buyUSDG(address _token, address _receiver) external returns (uint256); function sellUSDG(address _token, address _receiver) external returns (uint256); function swap(address _tokenIn, address _tokenOut, address _receiver) external returns (uint256); function increasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external; function decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external returns (uint256); function validateLiquidation(address _account, address _collateralToken, address _indexToken, bool _isLong, bool _raise) external view returns (uint256, uint256); function liquidatePosition(address _account, address _collateralToken, address _indexToken, bool _isLong, address _feeReceiver) external; function tokenToUsdMin(address _token, uint256 _tokenAmount) external view returns (uint256); function priceFeed() external view returns (address); function fundingRateFactor() external view returns (uint256); function stableFundingRateFactor() external view returns (uint256); function cumulativeFundingRates(address _token) external view returns (uint256); function getNextFundingRate(address _token) external view returns (uint256); function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256); function liquidationFeeUsd() external view returns (uint256); function taxBasisPoints() external view returns (uint256); function stableTaxBasisPoints() external view returns (uint256); function mintBurnFeeBasisPoints() external view returns (uint256); function swapFeeBasisPoints() external view returns (uint256); function stableSwapFeeBasisPoints() external view returns (uint256); function marginFeeBasisPoints() external view returns (uint256); function allWhitelistedTokensLength() external view returns (uint256); function allWhitelistedTokens(uint256) external view returns (address); function whitelistedTokens(address _token) external view returns (bool); function stableTokens(address _token) external view returns (bool); function shortableTokens(address _token) external view returns (bool); function feeReserves(address _token) external view returns (uint256); function globalShortSizes(address _token) external view returns (uint256); function globalShortAveragePrices(address _token) external view returns (uint256); function maxGlobalShortSizes(address _token) external view returns (uint256); function tokenDecimals(address _token) external view returns (uint256); function tokenWeights(address _token) external view returns (uint256); function guaranteedUsd(address _token) external view returns (uint256); function poolAmounts(address _token) external view returns (uint256); function bufferAmounts(address _token) external view returns (uint256); function reservedAmounts(address _token) external view returns (uint256); function usdgAmounts(address _token) external view returns (uint256); function maxUsdgAmounts(address _token) external view returns (uint256); function getRedemptionAmount(address _token, uint256 _usdgAmount) external view returns (uint256); function getMaxPrice(address _token) external view returns (uint256); function getMinPrice(address _token) external view returns (uint256); function getDelta(address _indexToken, uint256 _size, uint256 _averagePrice, bool _isLong, uint256 _lastIncreasedTime) external view returns (bool, uint256); function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong) external view returns (uint256, uint256, uint256, uint256, uint256, uint256, bool, uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IVaultUtils { function updateCumulativeFundingRate(address _collateralToken, address _indexToken) external returns (bool); function validateIncreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external view; function validateDecreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external view; function validateLiquidation(address _account, address _collateralToken, address _indexToken, bool _isLong, bool _raise) external view returns (uint256, uint256); function getEntryFundingRate(address _collateralToken, address _indexToken, bool _isLong) external view returns (uint256); function getPositionFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta) external view returns (uint256); function getFundingFee(address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _size, uint256 _entryFundingRate) external view returns (uint256); function getBuyUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) external view returns (uint256); function getSellUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) external view returns (uint256); function getSwapFeeBasisPoints(address _tokenIn, address _tokenOut, uint256 _usdgAmount) external view returns (uint256); function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./IVault.sol"; interface IGlpManager { function glp() external view returns (address); function usdg() external view returns (address); function vault() external view returns (IVault); function cooldownDuration() external returns (uint256); function getAumInUsdg(bool maximise) external view returns (uint256); function lastAddedAt(address _account) external returns (uint256); function addLiquidity(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external returns (uint256); function addLiquidityForAccount(address _fundingAccount, address _account, address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external returns (uint256); function removeLiquidity(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external returns (uint256); function removeLiquidityForAccount(address _account, address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external returns (uint256); function setShortsTrackerAveragePriceWeight(uint256 _shortsTrackerAveragePriceWeight) external; function setCooldownDuration(uint256 _cooldownDuration) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IReferralStorage { function codeOwners(bytes32 _code) external view returns (address); function traderReferralCodes(address _account) external view returns (bytes32); function referrerDiscountShares(address _account) external view returns (uint256); function referrerTiers(address _account) external view returns (uint256); function getTraderReferralInfo(address _account) external view returns (bytes32, address); function setTraderReferralCode(address _account, bytes32 _code) external; function setTier(uint256 _tierId, uint256 _totalRebate, uint256 _discountShare) external; function setReferrerTier(address _referrer, uint256 _tierId) external; function govSetCodeOwner(bytes32 _code, address _newAccount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IMintable { function isMinter(address _account) external returns (bool); function setMinter(address _minter, bool _isActive) external; function mint(address _account, uint256 _amount) external; function burn(address _account, uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IVester { function rewardTracker() external view returns (address); function claimForAccount(address _account, address _receiver) external returns (uint256); function claimable(address _account) external view returns (uint256); function cumulativeClaimAmounts(address _account) external view returns (uint256); function claimedAmounts(address _account) external view returns (uint256); function pairAmounts(address _account) external view returns (uint256); function getVestedAmount(address _account) external view returns (uint256); function transferredAverageStakedAmounts(address _account) external view returns (uint256); function transferredCumulativeRewards(address _account) external view returns (uint256); function cumulativeRewardDeductions(address _account) external view returns (uint256); function bonusRewards(address _account) external view returns (uint256); function transferStakeValues(address _sender, address _receiver) external; function setTransferredAverageStakedAmounts(address _account, uint256 _amount) external; function setTransferredCumulativeRewards(address _account, uint256 _amount) external; function setCumulativeRewardDeductions(address _account, uint256 _amount) external; function setBonusRewards(address _account, uint256 _amount) external; function getMaxVestableAmount(address _account) external view returns (uint256); function getCombinedAverageStakedAmount(address _account) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IRewardRouterV2 { function feeGlpTracker() external view returns (address); function stakedGlpTracker() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./YieldToken.sol"; contract YieldFarm is YieldToken, ReentrancyGuard { using SafeERC20 for IERC20; address public stakingToken; constructor(string memory _name, string memory _symbol, address _stakingToken) public YieldToken(_name, _symbol, 0) { stakingToken = _stakingToken; } function stake(uint256 _amount) external nonReentrant { IERC20(stakingToken).safeTransferFrom(msg.sender, address(this), _amount); _mint(msg.sender, _amount); } function unstake(uint256 _amount) external nonReentrant { _burn(msg.sender, _amount); IERC20(stakingToken).safeTransfer(msg.sender, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "./interfaces/IDistributor.sol"; contract TimeDistributor is IDistributor { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant DISTRIBUTION_INTERVAL = 1 hours; address public gov; address public admin; mapping (address => address) public rewardTokens; mapping (address => uint256) public override tokensPerInterval; mapping (address => uint256) public lastDistributionTime; event Distribute(address receiver, uint256 amount); event DistributionChange(address receiver, uint256 amount, address rewardToken); event TokensPerIntervalChange(address receiver, uint256 amount); modifier onlyGov() { require(msg.sender == gov, "TimeDistributor: forbidden"); _; } modifier onlyAdmin() { require(msg.sender == admin, "TimeDistributor: forbidden"); _; } constructor() public { gov = msg.sender; admin = msg.sender; } function setGov(address _gov) external onlyGov { gov = _gov; } function setTokensPerInterval(address _receiver, uint256 _amount) external onlyAdmin { if (lastDistributionTime[_receiver] != 0) { uint256 intervals = getIntervals(_receiver); require(intervals == 0, "TimeDistributor: pending distribution"); } tokensPerInterval[_receiver] = _amount; _updateLastDistributionTime(_receiver); emit TokensPerIntervalChange(_receiver, _amount); } function updateLastDistributionTime(address _receiver) external onlyAdmin { _updateLastDistributionTime(_receiver); } function setDistribution( address[] calldata _receivers, uint256[] calldata _amounts, address[] calldata _rewardTokens ) external onlyGov { for (uint256 i = 0; i < _receivers.length; i++) { address receiver = _receivers[i]; if (lastDistributionTime[receiver] != 0) { uint256 intervals = getIntervals(receiver); require(intervals == 0, "TimeDistributor: pending distribution"); } uint256 amount = _amounts[i]; address rewardToken = _rewardTokens[i]; tokensPerInterval[receiver] = amount; rewardTokens[receiver] = rewardToken; _updateLastDistributionTime(receiver); emit DistributionChange(receiver, amount, rewardToken); } } function distribute() external override returns (uint256) { address receiver = msg.sender; uint256 intervals = getIntervals(receiver); if (intervals == 0) { return 0; } uint256 amount = getDistributionAmount(receiver); _updateLastDistributionTime(receiver); if (amount == 0) { return 0; } IERC20(rewardTokens[receiver]).safeTransfer(receiver, amount); emit Distribute(receiver, amount); return amount; } function getRewardToken(address _receiver) external override view returns (address) { return rewardTokens[_receiver]; } function getDistributionAmount(address _receiver) public override view returns (uint256) { uint256 _tokensPerInterval = tokensPerInterval[_receiver]; if (_tokensPerInterval == 0) { return 0; } uint256 intervals = getIntervals(_receiver); uint256 amount = _tokensPerInterval.mul(intervals); if (IERC20(rewardTokens[_receiver]).balanceOf(address(this)) < amount) { return 0; } return amount; } function getIntervals(address _receiver) public view returns (uint256) { uint256 timeDiff = block.timestamp.sub(lastDistributionTime[_receiver]); return timeDiff.div(DISTRIBUTION_INTERVAL); } function _updateLastDistributionTime(address _receiver) private { lastDistributionTime[_receiver] = block.timestamp.div(DISTRIBUTION_INTERVAL).mul(DISTRIBUTION_INTERVAL); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract WETH is IERC20 { using SafeMath for uint256; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name, string memory symbol, uint8 decimals ) public { _name = name; _symbol = symbol; _decimals = decimals; } function deposit() public payable { _balances[msg.sender] = _balances[msg.sender].add(msg.value); } function withdraw(uint256 amount) public { require(_balances[msg.sender] >= amount); _balances[msg.sender] = _balances[msg.sender].sub(amount); msg.sender.transfer(amount); } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract Token is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor() public { _name = "Token"; _symbol = "TOKEN"; _decimals = 18; } function mint(address account, uint256 amount) public { _mint(account, amount); } function withdrawToken(address token, address account, uint256 amount) public { IERC20(token).transfer(account, amount); } function deposit() public payable { _mint(msg.sender, msg.value); } function withdraw(uint256 amount) public { require(_balances[msg.sender] >= amount, "Token: insufficient balance"); _burn(msg.sender, amount); msg.sender.transfer(amount); } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../tokens/Token.sol"; import "../libraries/token/IERC20.sol"; import "./interfaces/IPancakeRouter.sol"; contract PancakeRouter is IPancakeRouter { address public pair; constructor(address _pair) public { pair = _pair; } function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 /*amountAMin*/, uint256 /*amountBMin*/, address to, uint256 deadline ) external override returns (uint256 amountA, uint256 amountB, uint256 liquidity) { require(deadline >= block.timestamp, 'PancakeRouter: EXPIRED'); Token(pair).mint(to, 1000); IERC20(tokenA).transferFrom(msg.sender, pair, amountADesired); IERC20(tokenB).transferFrom(msg.sender, pair, amountBDesired); amountA = amountADesired; amountB = amountBDesired; liquidity = 1000; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPancakeRouter { function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../amm/interfaces/IPancakeRouter.sol"; import "./interfaces/IGMT.sol"; import "../peripherals/interfaces/ITimelockTarget.sol"; contract Treasury is ReentrancyGuard, ITimelockTarget { using SafeMath for uint256; uint256 constant PRECISION = 1000000; uint256 constant BASIS_POINTS_DIVISOR = 10000; bool public isInitialized; bool public isSwapActive = true; bool public isLiquidityAdded = false; address public gmt; address public busd; address public router; address public fund; uint256 public gmtPresalePrice; uint256 public gmtListingPrice; uint256 public busdSlotCap; uint256 public busdHardCap; uint256 public busdBasisPoints; uint256 public unlockTime; uint256 public busdReceived; address public gov; mapping (address => uint256) public swapAmounts; mapping (address => bool) public swapWhitelist; modifier onlyGov() { require(msg.sender == gov, "Treasury: forbidden"); _; } constructor() public { gov = msg.sender; } function initialize( address[] memory _addresses, uint256[] memory _values ) external onlyGov { require(!isInitialized, "Treasury: already initialized"); isInitialized = true; gmt = _addresses[0]; busd = _addresses[1]; router = _addresses[2]; fund = _addresses[3]; gmtPresalePrice = _values[0]; gmtListingPrice = _values[1]; busdSlotCap = _values[2]; busdHardCap = _values[3]; busdBasisPoints = _values[4]; unlockTime = _values[5]; } function setGov(address _gov) external override onlyGov nonReentrant { gov = _gov; } function setFund(address _fund) external onlyGov nonReentrant { fund = _fund; } function extendUnlockTime(uint256 _unlockTime) external onlyGov nonReentrant { require(_unlockTime > unlockTime, "Treasury: invalid _unlockTime"); unlockTime = _unlockTime; } function addWhitelists(address[] memory _accounts) external onlyGov nonReentrant { for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; swapWhitelist[account] = true; } } function removeWhitelists(address[] memory _accounts) external onlyGov nonReentrant { for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; swapWhitelist[account] = false; } } function updateWhitelist(address prevAccount, address nextAccount) external onlyGov nonReentrant { require(swapWhitelist[prevAccount], "Treasury: invalid prevAccount"); swapWhitelist[prevAccount] = false; swapWhitelist[nextAccount] = true; } function swap(uint256 _busdAmount) external nonReentrant { address account = msg.sender; require(swapWhitelist[account], "Treasury: forbidden"); require(isSwapActive, "Treasury: swap is no longer active"); require(_busdAmount > 0, "Treasury: invalid _busdAmount"); busdReceived = busdReceived.add(_busdAmount); require(busdReceived <= busdHardCap, "Treasury: busdHardCap exceeded"); swapAmounts[account] = swapAmounts[account].add(_busdAmount); require(swapAmounts[account] <= busdSlotCap, "Treasury: busdSlotCap exceeded"); // receive BUSD uint256 busdBefore = IERC20(busd).balanceOf(address(this)); IERC20(busd).transferFrom(account, address(this), _busdAmount); uint256 busdAfter = IERC20(busd).balanceOf(address(this)); require(busdAfter.sub(busdBefore) == _busdAmount, "Treasury: invalid transfer"); // send GMT uint256 gmtAmount = _busdAmount.mul(PRECISION).div(gmtPresalePrice); IERC20(gmt).transfer(account, gmtAmount); } function addLiquidity() external onlyGov nonReentrant { require(!isLiquidityAdded, "Treasury: liquidity already added"); isLiquidityAdded = true; uint256 busdAmount = busdReceived.mul(busdBasisPoints).div(BASIS_POINTS_DIVISOR); uint256 gmtAmount = busdAmount.mul(PRECISION).div(gmtListingPrice); IERC20(busd).approve(router, busdAmount); IERC20(gmt).approve(router, gmtAmount); IGMT(gmt).endMigration(); IPancakeRouter(router).addLiquidity( busd, // tokenA gmt, // tokenB busdAmount, // amountADesired gmtAmount, // amountBDesired 0, // amountAMin 0, // amountBMin address(this), // to block.timestamp // deadline ); IGMT(gmt).beginMigration(); uint256 fundAmount = busdReceived.sub(busdAmount); IERC20(busd).transfer(fund, fundAmount); } function withdrawToken(address _token, address _account, uint256 _amount) external override onlyGov nonReentrant { require(block.timestamp > unlockTime, "Treasury: unlockTime not yet passed"); IERC20(_token).transfer(_account, _amount); } function increaseBusdBasisPoints(uint256 _busdBasisPoints) external onlyGov nonReentrant { require(_busdBasisPoints > busdBasisPoints, "Treasury: invalid _busdBasisPoints"); busdBasisPoints = _busdBasisPoints; } function endSwap() external onlyGov nonReentrant { isSwapActive = false; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IGMT { function beginMigration() external; function endMigration() external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/ITimelockTarget.sol"; import "./interfaces/IHandlerTarget.sol"; import "../access/interfaces/IAdmin.sol"; import "../core/interfaces/IVaultPriceFeed.sol"; import "../oracle/interfaces/IFastPriceFeed.sol"; import "../referrals/interfaces/IReferralStorage.sol"; import "../tokens/interfaces/IYieldToken.sol"; import "../tokens/interfaces/IBaseToken.sol"; import "../tokens/interfaces/IMintable.sol"; import "../tokens/interfaces/IUSDG.sol"; import "../staking/interfaces/IVester.sol"; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; contract PriceFeedTimelock { using SafeMath for uint256; uint256 public constant MAX_BUFFER = 5 days; uint256 public buffer; address public admin; address public tokenManager; mapping (bytes32 => uint256) public pendingActions; mapping (address => bool) public isHandler; mapping (address => bool) public isKeeper; event SignalPendingAction(bytes32 action); event SignalApprove(address token, address spender, uint256 amount, bytes32 action); event SignalWithdrawToken(address target, address token, address receiver, uint256 amount, bytes32 action); event SignalSetGov(address target, address gov, bytes32 action); event SignalSetPriceFeedWatcher(address fastPriceFeed, address account, bool isActive); event SignalPriceFeedSetTokenConfig( address vaultPriceFeed, address token, address priceFeed, uint256 priceDecimals, bool isStrictStable ); event ClearAction(bytes32 action); modifier onlyAdmin() { require(msg.sender == admin, "Timelock: forbidden"); _; } modifier onlyHandlerAndAbove() { require(msg.sender == admin || isHandler[msg.sender], "Timelock: forbidden"); _; } modifier onlyKeeperAndAbove() { require(msg.sender == admin || isHandler[msg.sender] || isKeeper[msg.sender], "Timelock: forbidden"); _; } modifier onlyTokenManager() { require(msg.sender == tokenManager, "Timelock: forbidden"); _; } constructor( address _admin, uint256 _buffer, address _tokenManager ) public { require(_buffer <= MAX_BUFFER, "Timelock: invalid _buffer"); admin = _admin; buffer = _buffer; tokenManager = _tokenManager; } function setAdmin(address _admin) external onlyTokenManager { admin = _admin; } function setExternalAdmin(address _target, address _admin) external onlyAdmin { require(_target != address(this), "Timelock: invalid _target"); IAdmin(_target).setAdmin(_admin); } function setContractHandler(address _handler, bool _isActive) external onlyAdmin { isHandler[_handler] = _isActive; } function setKeeper(address _keeper, bool _isActive) external onlyAdmin { isKeeper[_keeper] = _isActive; } function setBuffer(uint256 _buffer) external onlyAdmin { require(_buffer <= MAX_BUFFER, "Timelock: invalid _buffer"); require(_buffer > buffer, "Timelock: buffer cannot be decreased"); buffer = _buffer; } function setIsAmmEnabled(address _priceFeed, bool _isEnabled) external onlyAdmin { IVaultPriceFeed(_priceFeed).setIsAmmEnabled(_isEnabled); } function setIsSecondaryPriceEnabled(address _priceFeed, bool _isEnabled) external onlyAdmin { IVaultPriceFeed(_priceFeed).setIsSecondaryPriceEnabled(_isEnabled); } function setMaxStrictPriceDeviation(address _priceFeed, uint256 _maxStrictPriceDeviation) external onlyAdmin { IVaultPriceFeed(_priceFeed).setMaxStrictPriceDeviation(_maxStrictPriceDeviation); } function setUseV2Pricing(address _priceFeed, bool _useV2Pricing) external onlyAdmin { IVaultPriceFeed(_priceFeed).setUseV2Pricing(_useV2Pricing); } function setAdjustment(address _priceFeed, address _token, bool _isAdditive, uint256 _adjustmentBps) external onlyKeeperAndAbove { IVaultPriceFeed(_priceFeed).setAdjustment(_token, _isAdditive, _adjustmentBps); } function setSpreadBasisPoints(address _priceFeed, address _token, uint256 _spreadBasisPoints) external onlyKeeperAndAbove { IVaultPriceFeed(_priceFeed).setSpreadBasisPoints(_token, _spreadBasisPoints); } function setPriceSampleSpace(address _priceFeed,uint256 _priceSampleSpace) external onlyHandlerAndAbove { require(_priceSampleSpace <= 5, "Invalid _priceSampleSpace"); IVaultPriceFeed(_priceFeed).setPriceSampleSpace(_priceSampleSpace); } function setVaultPriceFeed(address _fastPriceFeed, address _vaultPriceFeed) external onlyAdmin { IFastPriceFeed(_fastPriceFeed).setVaultPriceFeed(_vaultPriceFeed); } function setPriceDuration(address _fastPriceFeed, uint256 _priceDuration) external onlyHandlerAndAbove { IFastPriceFeed(_fastPriceFeed).setPriceDuration(_priceDuration); } function setMaxPriceUpdateDelay(address _fastPriceFeed, uint256 _maxPriceUpdateDelay) external onlyHandlerAndAbove { IFastPriceFeed(_fastPriceFeed).setMaxPriceUpdateDelay(_maxPriceUpdateDelay); } function setSpreadBasisPointsIfInactive(address _fastPriceFeed, uint256 _spreadBasisPointsIfInactive) external onlyAdmin { IFastPriceFeed(_fastPriceFeed).setSpreadBasisPointsIfInactive(_spreadBasisPointsIfInactive); } function setSpreadBasisPointsIfChainError(address _fastPriceFeed, uint256 _spreadBasisPointsIfChainError) external onlyAdmin { IFastPriceFeed(_fastPriceFeed).setSpreadBasisPointsIfChainError(_spreadBasisPointsIfChainError); } function setMinBlockInterval(address _fastPriceFeed, uint256 _minBlockInterval) external onlyAdmin { IFastPriceFeed(_fastPriceFeed).setMinBlockInterval(_minBlockInterval); } function setIsSpreadEnabled(address _fastPriceFeed, bool _isSpreadEnabled) external onlyAdmin { IFastPriceFeed(_fastPriceFeed).setIsSpreadEnabled(_isSpreadEnabled); } function transferIn(address _sender, address _token, uint256 _amount) external onlyAdmin { IERC20(_token).transferFrom(_sender, address(this), _amount); } function signalApprove(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _setPendingAction(action); emit SignalApprove(_token, _spender, _amount, action); } function approve(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _validateAction(action); _clearAction(action); IERC20(_token).approve(_spender, _amount); } function signalWithdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _setPendingAction(action); emit SignalWithdrawToken(_target, _token, _receiver, _amount, action); } function withdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _validateAction(action); _clearAction(action); IBaseToken(_target).withdrawToken(_token, _receiver, _amount); } function signalSetGov(address _target, address _gov) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _setPendingAction(action); emit SignalSetGov(_target, _gov, action); } function setGov(address _target, address _gov) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _validateAction(action); _clearAction(action); ITimelockTarget(_target).setGov(_gov); } function signalSetPriceFeedWatcher(address _fastPriceFeed, address _account, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeedWatcher", _fastPriceFeed, _account, _isActive)); _setPendingAction(action); emit SignalSetPriceFeedWatcher(_fastPriceFeed, _account, _isActive); } function setPriceFeedWatcher(address _fastPriceFeed, address _account, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeedWatcher", _fastPriceFeed, _account, _isActive)); _validateAction(action); _clearAction(action); IFastPriceFeed(_fastPriceFeed).setSigner(_account, _isActive); } function signalSetPriceFeedUpdater(address _fastPriceFeed, address _account, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeedUpdater", _fastPriceFeed, _account, _isActive)); _setPendingAction(action); emit SignalSetPriceFeedWatcher(_fastPriceFeed, _account, _isActive); } function setPriceFeedUpdater(address _fastPriceFeed, address _account, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeedUpdater", _fastPriceFeed, _account, _isActive)); _validateAction(action); _clearAction(action); IFastPriceFeed(_fastPriceFeed).setUpdater(_account, _isActive); } function signalPriceFeedSetTokenConfig( address _vaultPriceFeed, address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "priceFeedSetTokenConfig", _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable )); _setPendingAction(action); emit SignalPriceFeedSetTokenConfig( _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable ); } function priceFeedSetTokenConfig( address _vaultPriceFeed, address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "priceFeedSetTokenConfig", _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable )); _validateAction(action); _clearAction(action); IVaultPriceFeed(_vaultPriceFeed).setTokenConfig( _token, _priceFeed, _priceDecimals, _isStrictStable ); } function cancelAction(bytes32 _action) external onlyAdmin { _clearAction(_action); } function _setPendingAction(bytes32 _action) private { pendingActions[_action] = block.timestamp.add(buffer); emit SignalPendingAction(_action); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action] != 0, "Timelock: action not signalled"); require(pendingActions[_action] < block.timestamp, "Timelock: action time not yet passed"); } function _clearAction(bytes32 _action) private { require(pendingActions[_action] != 0, "Timelock: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IVaultPriceFeed { function adjustmentBasisPoints(address _token) external view returns (uint256); function isAdjustmentAdditive(address _token) external view returns (bool); function setAdjustment(address _token, bool _isAdditive, uint256 _adjustmentBps) external; function setUseV2Pricing(bool _useV2Pricing) external; function setIsAmmEnabled(bool _isEnabled) external; function setIsSecondaryPriceEnabled(bool _isEnabled) external; function setSpreadBasisPoints(address _token, uint256 _spreadBasisPoints) external; function setSpreadThresholdBasisPoints(uint256 _spreadThresholdBasisPoints) external; function setFavorPrimaryPrice(bool _favorPrimaryPrice) external; function setPriceSampleSpace(uint256 _priceSampleSpace) external; function setMaxStrictPriceDeviation(uint256 _maxStrictPriceDeviation) external; function getPrice(address _token, bool _maximise, bool _includeAmmPrice, bool _useSwapPricing) external view returns (uint256); function getAmmPrice(address _token) external view returns (uint256); function getLatestPrimaryPrice(address _token) external view returns (uint256); function getPrimaryPrice(address _token, bool _maximise) external view returns (uint256); function setTokenConfig( address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IFastPriceFeed { function lastUpdatedAt() external view returns (uint256); function lastUpdatedBlock() external view returns (uint256); function setSigner(address _account, bool _isActive) external; function setUpdater(address _account, bool _isActive) external; function setPriceDuration(uint256 _priceDuration) external; function setMaxPriceUpdateDelay(uint256 _maxPriceUpdateDelay) external; function setSpreadBasisPointsIfInactive(uint256 _spreadBasisPointsIfInactive) external; function setSpreadBasisPointsIfChainError(uint256 _spreadBasisPointsIfChainError) external; function setMinBlockInterval(uint256 _minBlockInterval) external; function setIsSpreadEnabled(bool _isSpreadEnabled) external; function setMaxDeviationBasisPoints(uint256 _maxDeviationBasisPoints) external; function setMaxCumulativeDeltaDiffs(address[] memory _tokens, uint256[] memory _maxCumulativeDeltaDiffs) external; function setPriceDataInterval(uint256 _priceDataInterval) external; function setVaultPriceFeed(address _vaultPriceFeed) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRewardTracker.sol"; import "./interfaces/IVester.sol"; import "../tokens/interfaces/IMintable.sol"; import "../access/Governable.sol"; contract Vester is IVester, IERC20, ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; string public name; string public symbol; uint8 public decimals = 18; uint256 public vestingDuration; address public esToken; address public pairToken; address public claimableToken; address public override rewardTracker; uint256 public override totalSupply; uint256 public pairSupply; bool public hasMaxVestableAmount; mapping (address => uint256) public balances; mapping (address => uint256) public override pairAmounts; mapping (address => uint256) public override cumulativeClaimAmounts; mapping (address => uint256) public override claimedAmounts; mapping (address => uint256) public lastVestingTimes; mapping (address => uint256) public override transferredAverageStakedAmounts; mapping (address => uint256) public override transferredCumulativeRewards; mapping (address => uint256) public override cumulativeRewardDeductions; mapping (address => uint256) public override bonusRewards; mapping (address => bool) public isHandler; event Claim(address receiver, uint256 amount); event Deposit(address account, uint256 amount); event Withdraw(address account, uint256 claimedAmount, uint256 balance); event PairTransfer(address indexed from, address indexed to, uint256 value); constructor ( string memory _name, string memory _symbol, uint256 _vestingDuration, address _esToken, address _pairToken, address _claimableToken, address _rewardTracker ) public { name = _name; symbol = _symbol; vestingDuration = _vestingDuration; esToken = _esToken; pairToken = _pairToken; claimableToken = _claimableToken; rewardTracker = _rewardTracker; if (rewardTracker != address(0)) { hasMaxVestableAmount = true; } } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; } function setHasMaxVestableAmount(bool _hasMaxVestableAmount) external onlyGov { hasMaxVestableAmount = _hasMaxVestableAmount; } function deposit(uint256 _amount) external nonReentrant { _deposit(msg.sender, _amount); } function depositForAccount(address _account, uint256 _amount) external nonReentrant { _validateHandler(); _deposit(_account, _amount); } function claim() external nonReentrant returns (uint256) { return _claim(msg.sender, msg.sender); } function claimForAccount(address _account, address _receiver) external override nonReentrant returns (uint256) { _validateHandler(); return _claim(_account, _receiver); } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function withdraw() external nonReentrant { address account = msg.sender; address _receiver = account; _claim(account, _receiver); uint256 claimedAmount = cumulativeClaimAmounts[account]; uint256 balance = balances[account]; uint256 totalVested = balance.add(claimedAmount); require(totalVested > 0, "Vester: vested amount is zero"); if (hasPairToken()) { uint256 pairAmount = pairAmounts[account]; _burnPair(account, pairAmount); IERC20(pairToken).safeTransfer(_receiver, pairAmount); } IERC20(esToken).safeTransfer(_receiver, balance); _burn(account, balance); delete cumulativeClaimAmounts[account]; delete claimedAmounts[account]; delete lastVestingTimes[account]; emit Withdraw(account, claimedAmount, balance); } function transferStakeValues(address _sender, address _receiver) external override nonReentrant { _validateHandler(); transferredAverageStakedAmounts[_receiver] = getCombinedAverageStakedAmount(_sender); transferredAverageStakedAmounts[_sender] = 0; uint256 transferredCumulativeReward = transferredCumulativeRewards[_sender]; uint256 cumulativeReward = IRewardTracker(rewardTracker).cumulativeRewards(_sender); transferredCumulativeRewards[_receiver] = transferredCumulativeReward.add(cumulativeReward); cumulativeRewardDeductions[_sender] = cumulativeReward; transferredCumulativeRewards[_sender] = 0; bonusRewards[_receiver] = bonusRewards[_sender]; bonusRewards[_sender] = 0; } function setTransferredAverageStakedAmounts(address _account, uint256 _amount) external override nonReentrant { _validateHandler(); transferredAverageStakedAmounts[_account] = _amount; } function setTransferredCumulativeRewards(address _account, uint256 _amount) external override nonReentrant { _validateHandler(); transferredCumulativeRewards[_account] = _amount; } function setCumulativeRewardDeductions(address _account, uint256 _amount) external override nonReentrant { _validateHandler(); cumulativeRewardDeductions[_account] = _amount; } function setBonusRewards(address _account, uint256 _amount) external override nonReentrant { _validateHandler(); bonusRewards[_account] = _amount; } function claimable(address _account) public override view returns (uint256) { uint256 amount = cumulativeClaimAmounts[_account].sub(claimedAmounts[_account]); uint256 nextClaimable = _getNextClaimableAmount(_account); return amount.add(nextClaimable); } function getMaxVestableAmount(address _account) public override view returns (uint256) { if (!hasRewardTracker()) { return 0; } uint256 transferredCumulativeReward = transferredCumulativeRewards[_account]; uint256 bonusReward = bonusRewards[_account]; uint256 cumulativeReward = IRewardTracker(rewardTracker).cumulativeRewards(_account); uint256 maxVestableAmount = cumulativeReward.add(transferredCumulativeReward).add(bonusReward); uint256 cumulativeRewardDeduction = cumulativeRewardDeductions[_account]; if (maxVestableAmount < cumulativeRewardDeduction) { return 0; } return maxVestableAmount.sub(cumulativeRewardDeduction); } function getCombinedAverageStakedAmount(address _account) public override view returns (uint256) { uint256 cumulativeReward = IRewardTracker(rewardTracker).cumulativeRewards(_account); uint256 transferredCumulativeReward = transferredCumulativeRewards[_account]; uint256 totalCumulativeReward = cumulativeReward.add(transferredCumulativeReward); if (totalCumulativeReward == 0) { return 0; } uint256 averageStakedAmount = IRewardTracker(rewardTracker).averageStakedAmounts(_account); uint256 transferredAverageStakedAmount = transferredAverageStakedAmounts[_account]; return averageStakedAmount .mul(cumulativeReward) .div(totalCumulativeReward) .add( transferredAverageStakedAmount.mul(transferredCumulativeReward).div(totalCumulativeReward) ); } function getPairAmount(address _account, uint256 _esAmount) public view returns (uint256) { if (!hasRewardTracker()) { return 0; } uint256 combinedAverageStakedAmount = getCombinedAverageStakedAmount(_account); if (combinedAverageStakedAmount == 0) { return 0; } uint256 maxVestableAmount = getMaxVestableAmount(_account); if (maxVestableAmount == 0) { return 0; } return _esAmount.mul(combinedAverageStakedAmount).div(maxVestableAmount); } function hasRewardTracker() public view returns (bool) { return rewardTracker != address(0); } function hasPairToken() public view returns (bool) { return pairToken != address(0); } function getTotalVested(address _account) public view returns (uint256) { return balances[_account].add(cumulativeClaimAmounts[_account]); } function balanceOf(address _account) public view override returns (uint256) { return balances[_account]; } // empty implementation, tokens are non-transferrable function transfer(address /* recipient */, uint256 /* amount */) public override returns (bool) { revert("Vester: non-transferrable"); } // empty implementation, tokens are non-transferrable function allowance(address /* owner */, address /* spender */) public view virtual override returns (uint256) { return 0; } // empty implementation, tokens are non-transferrable function approve(address /* spender */, uint256 /* amount */) public virtual override returns (bool) { revert("Vester: non-transferrable"); } // empty implementation, tokens are non-transferrable function transferFrom(address /* sender */, address /* recipient */, uint256 /* amount */) public virtual override returns (bool) { revert("Vester: non-transferrable"); } function getVestedAmount(address _account) public override view returns (uint256) { uint256 balance = balances[_account]; uint256 cumulativeClaimAmount = cumulativeClaimAmounts[_account]; return balance.add(cumulativeClaimAmount); } function _mint(address _account, uint256 _amount) private { require(_account != address(0), "Vester: mint to the zero address"); totalSupply = totalSupply.add(_amount); balances[_account] = balances[_account].add(_amount); emit Transfer(address(0), _account, _amount); } function _mintPair(address _account, uint256 _amount) private { require(_account != address(0), "Vester: mint to the zero address"); pairSupply = pairSupply.add(_amount); pairAmounts[_account] = pairAmounts[_account].add(_amount); emit PairTransfer(address(0), _account, _amount); } function _burn(address _account, uint256 _amount) private { require(_account != address(0), "Vester: burn from the zero address"); balances[_account] = balances[_account].sub(_amount, "Vester: burn amount exceeds balance"); totalSupply = totalSupply.sub(_amount); emit Transfer(_account, address(0), _amount); } function _burnPair(address _account, uint256 _amount) private { require(_account != address(0), "Vester: burn from the zero address"); pairAmounts[_account] = pairAmounts[_account].sub(_amount, "Vester: burn amount exceeds balance"); pairSupply = pairSupply.sub(_amount); emit PairTransfer(_account, address(0), _amount); } function _deposit(address _account, uint256 _amount) private { require(_amount > 0, "Vester: invalid _amount"); _updateVesting(_account); IERC20(esToken).safeTransferFrom(_account, address(this), _amount); _mint(_account, _amount); if (hasPairToken()) { uint256 pairAmount = pairAmounts[_account]; uint256 nextPairAmount = getPairAmount(_account, balances[_account]); if (nextPairAmount > pairAmount) { uint256 pairAmountDiff = nextPairAmount.sub(pairAmount); IERC20(pairToken).safeTransferFrom(_account, address(this), pairAmountDiff); _mintPair(_account, pairAmountDiff); } } if (hasMaxVestableAmount) { uint256 maxAmount = getMaxVestableAmount(_account); require(getTotalVested(_account) <= maxAmount, "Vester: max vestable amount exceeded"); } emit Deposit(_account, _amount); } function _updateVesting(address _account) private { uint256 amount = _getNextClaimableAmount(_account); lastVestingTimes[_account] = block.timestamp; if (amount == 0) { return; } // transfer claimableAmount from balances to cumulativeClaimAmounts _burn(_account, amount); cumulativeClaimAmounts[_account] = cumulativeClaimAmounts[_account].add(amount); IMintable(esToken).burn(address(this), amount); } function _getNextClaimableAmount(address _account) private view returns (uint256) { uint256 timeDiff = block.timestamp.sub(lastVestingTimes[_account]); uint256 balance = balances[_account]; if (balance == 0) { return 0; } uint256 vestedAmount = getVestedAmount(_account); uint256 claimableAmount = vestedAmount.mul(timeDiff).div(vestingDuration); if (claimableAmount < balance) { return claimableAmount; } return balance; } function _claim(address _account, address _receiver) private returns (uint256) { _updateVesting(_account); uint256 amount = claimable(_account); claimedAmounts[_account] = claimedAmounts[_account].add(amount); IERC20(claimableToken).safeTransfer(_receiver, amount); emit Claim(_account, amount); return amount; } function _validateHandler() private view { require(isHandler[msg.sender], "Vester: forbidden"); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IRewardTracker { function depositBalances(address _account, address _depositToken) external view returns (uint256); function stakedAmounts(address _account) external view returns (uint256); function updateRewards() external; function stake(address _depositToken, uint256 _amount) external; function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount) external; function unstake(address _depositToken, uint256 _amount) external; function unstakeForAccount(address _account, address _depositToken, uint256 _amount, address _receiver) external; function tokensPerInterval() external view returns (uint256); function claim(address _receiver) external returns (uint256); function claimForAccount(address _account, address _receiver) external returns (uint256); function claimable(address _account) external view returns (uint256); function averageStakedAmounts(address _account) external view returns (uint256); function cumulativeRewards(address _account) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./BaseToken.sol"; import "./interfaces/IMintable.sol"; contract MintableBaseToken is BaseToken, IMintable { mapping (address => bool) public override isMinter; constructor(string memory _name, string memory _symbol, uint256 _initialSupply) public BaseToken(_name, _symbol, _initialSupply) { } modifier onlyMinter() { require(isMinter[msg.sender], "MintableBaseToken: forbidden"); _; } function setMinter(address _minter, bool _isActive) external override onlyGov { isMinter[_minter] = _isActive; } function mint(address _account, uint256 _amount) external override onlyMinter { _mint(_account, _amount); } function burn(address _account, uint256 _amount) external override onlyMinter { _burn(_account, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "./interfaces/IYieldTracker.sol"; import "./interfaces/IBaseToken.sol"; contract BaseToken is IERC20, IBaseToken { using SafeMath for uint256; using SafeERC20 for IERC20; string public name; string public symbol; uint8 public constant decimals = 18; uint256 public override totalSupply; uint256 public nonStakingSupply; address public gov; mapping (address => uint256) public balances; mapping (address => mapping (address => uint256)) public allowances; address[] public yieldTrackers; mapping (address => bool) public nonStakingAccounts; mapping (address => bool) public admins; bool public inPrivateTransferMode; mapping (address => bool) public isHandler; modifier onlyGov() { require(msg.sender == gov, "BaseToken: forbidden"); _; } modifier onlyAdmin() { require(admins[msg.sender], "BaseToken: forbidden"); _; } constructor(string memory _name, string memory _symbol, uint256 _initialSupply) public { name = _name; symbol = _symbol; gov = msg.sender; _mint(msg.sender, _initialSupply); } function setGov(address _gov) external onlyGov { gov = _gov; } function setInfo(string memory _name, string memory _symbol) external onlyGov { name = _name; symbol = _symbol; } function setYieldTrackers(address[] memory _yieldTrackers) external onlyGov { yieldTrackers = _yieldTrackers; } function addAdmin(address _account) external onlyGov { admins[_account] = true; } function removeAdmin(address _account) external override onlyGov { admins[_account] = false; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external override onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function setInPrivateTransferMode(bool _inPrivateTransferMode) external override onlyGov { inPrivateTransferMode = _inPrivateTransferMode; } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; } function addNonStakingAccount(address _account) external onlyAdmin { require(!nonStakingAccounts[_account], "BaseToken: _account already marked"); _updateRewards(_account); nonStakingAccounts[_account] = true; nonStakingSupply = nonStakingSupply.add(balances[_account]); } function removeNonStakingAccount(address _account) external onlyAdmin { require(nonStakingAccounts[_account], "BaseToken: _account not marked"); _updateRewards(_account); nonStakingAccounts[_account] = false; nonStakingSupply = nonStakingSupply.sub(balances[_account]); } function recoverClaim(address _account, address _receiver) external onlyAdmin { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).claim(_account, _receiver); } } function claim(address _receiver) external { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).claim(msg.sender, _receiver); } } function totalStaked() external view override returns (uint256) { return totalSupply.sub(nonStakingSupply); } function balanceOf(address _account) external view override returns (uint256) { return balances[_account]; } function stakedBalance(address _account) external view override returns (uint256) { if (nonStakingAccounts[_account]) { return 0; } return balances[_account]; } function transfer(address _recipient, uint256 _amount) external override returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function allowance(address _owner, address _spender) external view override returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external override returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) { if (isHandler[msg.sender]) { _transfer(_sender, _recipient, _amount); return true; } uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "BaseToken: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function _mint(address _account, uint256 _amount) internal { require(_account != address(0), "BaseToken: mint to the zero address"); _updateRewards(_account); totalSupply = totalSupply.add(_amount); balances[_account] = balances[_account].add(_amount); if (nonStakingAccounts[_account]) { nonStakingSupply = nonStakingSupply.add(_amount); } emit Transfer(address(0), _account, _amount); } function _burn(address _account, uint256 _amount) internal { require(_account != address(0), "BaseToken: burn from the zero address"); _updateRewards(_account); balances[_account] = balances[_account].sub(_amount, "BaseToken: burn amount exceeds balance"); totalSupply = totalSupply.sub(_amount); if (nonStakingAccounts[_account]) { nonStakingSupply = nonStakingSupply.sub(_amount); } emit Transfer(_account, address(0), _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "BaseToken: transfer from the zero address"); require(_recipient != address(0), "BaseToken: transfer to the zero address"); if (inPrivateTransferMode) { require(isHandler[msg.sender], "BaseToken: msg.sender not whitelisted"); } _updateRewards(_sender); _updateRewards(_recipient); balances[_sender] = balances[_sender].sub(_amount, "BaseToken: transfer amount exceeds balance"); balances[_recipient] = balances[_recipient].add(_amount); if (nonStakingAccounts[_sender]) { nonStakingSupply = nonStakingSupply.sub(_amount); } if (nonStakingAccounts[_recipient]) { nonStakingSupply = nonStakingSupply.add(_amount); } emit Transfer(_sender, _recipient,_amount); } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "BaseToken: approve from the zero address"); require(_spender != address(0), "BaseToken: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } function _updateRewards(address _account) private { for (uint256 i = 0; i < yieldTrackers.length; i++) { address yieldTracker = yieldTrackers[i]; IYieldTracker(yieldTracker).updateRewards(_account); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./MintableBaseToken.sol"; contract SnapshotToken is MintableBaseToken { constructor(string memory _name, string memory _symbol, uint256 _initialSupply) public MintableBaseToken(_name, _symbol, _initialSupply) { } function batchMint(address[] memory _accounts, uint256[] memory _amounts) external onlyMinter { for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; uint256 amount = _amounts[i]; _mint(account, amount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/ITimelockTarget.sol"; import "./interfaces/IGmxTimelock.sol"; import "./interfaces/IHandlerTarget.sol"; import "../access/interfaces/IAdmin.sol"; import "../core/interfaces/IVault.sol"; import "../core/interfaces/IVaultUtils.sol"; import "../core/interfaces/IVaultPriceFeed.sol"; import "../core/interfaces/IRouter.sol"; import "../tokens/interfaces/IYieldToken.sol"; import "../tokens/interfaces/IBaseToken.sol"; import "../tokens/interfaces/IMintable.sol"; import "../tokens/interfaces/IUSDG.sol"; import "../staking/interfaces/IVester.sol"; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; contract GmxTimelock is IGmxTimelock { using SafeMath for uint256; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant MAX_BUFFER = 7 days; uint256 public constant MAX_FEE_BASIS_POINTS = 300; // 3% uint256 public constant MAX_FUNDING_RATE_FACTOR = 200; // 0.02% uint256 public constant MAX_LEVERAGE_VALIDATION = 500000; // 50x uint256 public buffer; uint256 public longBuffer; address public admin; address public tokenManager; address public rewardManager; address public mintReceiver; uint256 public maxTokenSupply; mapping (bytes32 => uint256) public pendingActions; mapping (address => bool) public excludedTokens; mapping (address => bool) public isHandler; event SignalPendingAction(bytes32 action); event SignalApprove(address token, address spender, uint256 amount, bytes32 action); event SignalWithdrawToken(address target, address token, address receiver, uint256 amount, bytes32 action); event SignalMint(address token, address receiver, uint256 amount, bytes32 action); event SignalSetGov(address target, address gov, bytes32 action); event SignalSetPriceFeed(address vault, address priceFeed, bytes32 action); event SignalAddPlugin(address router, address plugin, bytes32 action); event SignalRedeemUsdg(address vault, address token, uint256 amount); event SignalVaultSetTokenConfig( address vault, address token, uint256 tokenDecimals, uint256 tokenWeight, uint256 minProfitBps, uint256 maxUsdgAmount, bool isStable, bool isShortable ); event SignalPriceFeedSetTokenConfig( address vaultPriceFeed, address token, address priceFeed, uint256 priceDecimals, bool isStrictStable ); event ClearAction(bytes32 action); modifier onlyAdmin() { require(msg.sender == admin, "GmxTimelock: forbidden"); _; } modifier onlyAdminOrHandler() { require(msg.sender == admin || isHandler[msg.sender], "GmxTimelock: forbidden"); _; } modifier onlyTokenManager() { require(msg.sender == tokenManager, "GmxTimelock: forbidden"); _; } modifier onlyRewardManager() { require(msg.sender == rewardManager, "GmxTimelock: forbidden"); _; } constructor( address _admin, uint256 _buffer, uint256 _longBuffer, address _rewardManager, address _tokenManager, address _mintReceiver, uint256 _maxTokenSupply ) public { require(_buffer <= MAX_BUFFER, "GmxTimelock: invalid _buffer"); require(_longBuffer <= MAX_BUFFER, "GmxTimelock: invalid _longBuffer"); admin = _admin; buffer = _buffer; longBuffer = _longBuffer; rewardManager = _rewardManager; tokenManager = _tokenManager; mintReceiver = _mintReceiver; maxTokenSupply = _maxTokenSupply; } function setAdmin(address _admin) external override onlyTokenManager { admin = _admin; } function setExternalAdmin(address _target, address _admin) external onlyAdmin { require(_target != address(this), "GmxTimelock: invalid _target"); IAdmin(_target).setAdmin(_admin); } function setContractHandler(address _handler, bool _isActive) external onlyAdmin { isHandler[_handler] = _isActive; } function setBuffer(uint256 _buffer) external onlyAdmin { require(_buffer <= MAX_BUFFER, "GmxTimelock: invalid _buffer"); require(_buffer > buffer, "GmxTimelock: buffer cannot be decreased"); buffer = _buffer; } function setMaxLeverage(address _vault, uint256 _maxLeverage) external onlyAdmin { require(_maxLeverage > MAX_LEVERAGE_VALIDATION, "GmxTimelock: invalid _maxLeverage"); IVault(_vault).setMaxLeverage(_maxLeverage); } function setFundingRate(address _vault, uint256 _fundingInterval, uint256 _fundingRateFactor, uint256 _stableFundingRateFactor) external onlyAdmin { require(_fundingRateFactor < MAX_FUNDING_RATE_FACTOR, "GmxTimelock: invalid _fundingRateFactor"); require(_stableFundingRateFactor < MAX_FUNDING_RATE_FACTOR, "GmxTimelock: invalid _stableFundingRateFactor"); IVault(_vault).setFundingRate(_fundingInterval, _fundingRateFactor, _stableFundingRateFactor); } function setFees( address _vault, uint256 _taxBasisPoints, uint256 _stableTaxBasisPoints, uint256 _mintBurnFeeBasisPoints, uint256 _swapFeeBasisPoints, uint256 _stableSwapFeeBasisPoints, uint256 _marginFeeBasisPoints, uint256 _liquidationFeeUsd, uint256 _minProfitTime, bool _hasDynamicFees ) external onlyAdmin { require(_taxBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _taxBasisPoints"); require(_stableTaxBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _stableTaxBasisPoints"); require(_mintBurnFeeBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _mintBurnFeeBasisPoints"); require(_swapFeeBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _swapFeeBasisPoints"); require(_stableSwapFeeBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _stableSwapFeeBasisPoints"); require(_marginFeeBasisPoints < MAX_FEE_BASIS_POINTS, "GmxTimelock: invalid _marginFeeBasisPoints"); require(_liquidationFeeUsd < 10 * PRICE_PRECISION, "GmxTimelock: invalid _liquidationFeeUsd"); IVault(_vault).setFees( _taxBasisPoints, _stableTaxBasisPoints, _mintBurnFeeBasisPoints, _swapFeeBasisPoints, _stableSwapFeeBasisPoints, _marginFeeBasisPoints, _liquidationFeeUsd, _minProfitTime, _hasDynamicFees ); } function setTokenConfig( address _vault, address _token, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, uint256 _bufferAmount, uint256 _usdgAmount ) external onlyAdmin { require(_minProfitBps <= 500, "GmxTimelock: invalid _minProfitBps"); IVault vault = IVault(_vault); require(vault.whitelistedTokens(_token), "GmxTimelock: token not yet whitelisted"); uint256 tokenDecimals = vault.tokenDecimals(_token); bool isStable = vault.stableTokens(_token); bool isShortable = vault.shortableTokens(_token); IVault(_vault).setTokenConfig( _token, tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, isStable, isShortable ); IVault(_vault).setBufferAmount(_token, _bufferAmount); IVault(_vault).setUsdgAmount(_token, _usdgAmount); } function setMaxGlobalShortSize(address _vault, address _token, uint256 _amount) external onlyAdmin { IVault(_vault).setMaxGlobalShortSize(_token, _amount); } function removeAdmin(address _token, address _account) external onlyAdmin { IYieldToken(_token).removeAdmin(_account); } function setIsAmmEnabled(address _priceFeed, bool _isEnabled) external onlyAdmin { IVaultPriceFeed(_priceFeed).setIsAmmEnabled(_isEnabled); } function setIsSecondaryPriceEnabled(address _priceFeed, bool _isEnabled) external onlyAdmin { IVaultPriceFeed(_priceFeed).setIsSecondaryPriceEnabled(_isEnabled); } function setMaxStrictPriceDeviation(address _priceFeed, uint256 _maxStrictPriceDeviation) external onlyAdmin { IVaultPriceFeed(_priceFeed).setMaxStrictPriceDeviation(_maxStrictPriceDeviation); } function setUseV2Pricing(address _priceFeed, bool _useV2Pricing) external onlyAdmin { IVaultPriceFeed(_priceFeed).setUseV2Pricing(_useV2Pricing); } function setAdjustment(address _priceFeed, address _token, bool _isAdditive, uint256 _adjustmentBps) external onlyAdmin { IVaultPriceFeed(_priceFeed).setAdjustment(_token, _isAdditive, _adjustmentBps); } function setSpreadBasisPoints(address _priceFeed, address _token, uint256 _spreadBasisPoints) external onlyAdmin { IVaultPriceFeed(_priceFeed).setSpreadBasisPoints(_token, _spreadBasisPoints); } function setSpreadThresholdBasisPoints(address _priceFeed, uint256 _spreadThresholdBasisPoints) external onlyAdmin { IVaultPriceFeed(_priceFeed).setSpreadThresholdBasisPoints(_spreadThresholdBasisPoints); } function setFavorPrimaryPrice(address _priceFeed, bool _favorPrimaryPrice) external onlyAdmin { IVaultPriceFeed(_priceFeed).setFavorPrimaryPrice(_favorPrimaryPrice); } function setPriceSampleSpace(address _priceFeed,uint256 _priceSampleSpace) external onlyAdmin { require(_priceSampleSpace <= 5, "Invalid _priceSampleSpace"); IVaultPriceFeed(_priceFeed).setPriceSampleSpace(_priceSampleSpace); } function setIsSwapEnabled(address _vault, bool _isSwapEnabled) external onlyAdmin { IVault(_vault).setIsSwapEnabled(_isSwapEnabled); } function setIsLeverageEnabled(address _vault, bool _isLeverageEnabled) external override onlyAdminOrHandler { IVault(_vault).setIsLeverageEnabled(_isLeverageEnabled); } function setVaultUtils(address _vault, IVaultUtils _vaultUtils) external onlyAdmin { IVault(_vault).setVaultUtils(_vaultUtils); } function setMaxGasPrice(address _vault,uint256 _maxGasPrice) external onlyAdmin { require(_maxGasPrice > 5000000000, "Invalid _maxGasPrice"); IVault(_vault).setMaxGasPrice(_maxGasPrice); } function withdrawFees(address _vault,address _token, address _receiver) external onlyAdmin { IVault(_vault).withdrawFees(_token, _receiver); } function setInPrivateLiquidationMode(address _vault, bool _inPrivateLiquidationMode) external onlyAdmin { IVault(_vault).setInPrivateLiquidationMode(_inPrivateLiquidationMode); } function setLiquidator(address _vault, address _liquidator, bool _isActive) external onlyAdmin { IVault(_vault).setLiquidator(_liquidator, _isActive); } function addExcludedToken(address _token) external onlyAdmin { excludedTokens[_token] = true; } function setInPrivateTransferMode(address _token, bool _inPrivateTransferMode) external onlyAdmin { if (excludedTokens[_token]) { // excludedTokens can only have their transfers enabled require(_inPrivateTransferMode == false, "GmxTimelock: invalid _inPrivateTransferMode"); } IBaseToken(_token).setInPrivateTransferMode(_inPrivateTransferMode); } function transferIn(address _sender, address _token, uint256 _amount) external onlyAdmin { IERC20(_token).transferFrom(_sender, address(this), _amount); } function signalApprove(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _setPendingAction(action); emit SignalApprove(_token, _spender, _amount, action); } function approve(address _token, address _spender, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount)); _validateAction(action); _clearAction(action); IERC20(_token).approve(_spender, _amount); } function signalWithdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _setPendingAction(action); emit SignalWithdrawToken(_target, _token, _receiver, _amount, action); } function withdrawToken(address _target, address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("withdrawToken", _target, _token, _receiver, _amount)); _validateAction(action); _clearAction(action); IBaseToken(_target).withdrawToken(_token, _receiver, _amount); } function signalMint(address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("mint", _token, _receiver, _amount)); _setPendingAction(action); emit SignalMint(_token, _receiver, _amount, action); } function processMint(address _token, address _receiver, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("mint", _token, _receiver, _amount)); _validateAction(action); _clearAction(action); _mint(_token, _receiver, _amount); } function signalSetGov(address _target, address _gov) external override onlyTokenManager { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _setLongPendingAction(action); emit SignalSetGov(_target, _gov, action); } function setGov(address _target, address _gov) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _target, _gov)); _validateAction(action); _clearAction(action); ITimelockTarget(_target).setGov(_gov); } function signalSetPriceFeed(address _vault, address _priceFeed) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeed", _vault, _priceFeed)); _setPendingAction(action); emit SignalSetPriceFeed(_vault, _priceFeed, action); } function setPriceFeed(address _vault, address _priceFeed) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setPriceFeed", _vault, _priceFeed)); _validateAction(action); _clearAction(action); IVault(_vault).setPriceFeed(_priceFeed); } function signalAddPlugin(address _router, address _plugin) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("addPlugin", _router, _plugin)); _setPendingAction(action); emit SignalAddPlugin(_router, _plugin, action); } function addPlugin(address _router, address _plugin) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("addPlugin", _router, _plugin)); _validateAction(action); _clearAction(action); IRouter(_router).addPlugin(_plugin); } function signalRedeemUsdg(address _vault, address _token, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("redeemUsdg", _vault, _token, _amount)); _setPendingAction(action); emit SignalRedeemUsdg(_vault, _token, _amount); } function redeemUsdg(address _vault, address _token, uint256 _amount) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("redeemUsdg", _vault, _token, _amount)); _validateAction(action); _clearAction(action); address usdg = IVault(_vault).usdg(); IVault(_vault).setManager(address(this), true); IUSDG(usdg).addVault(address(this)); IUSDG(usdg).mint(address(this), _amount); IERC20(usdg).transfer(address(_vault), _amount); IVault(_vault).sellUSDG(_token, mintReceiver); IVault(_vault).setManager(address(this), false); IUSDG(usdg).removeVault(address(this)); } function signalVaultSetTokenConfig( address _vault, address _token, uint256 _tokenDecimals, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "vaultSetTokenConfig", _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable )); _setPendingAction(action); emit SignalVaultSetTokenConfig( _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable ); } function vaultSetTokenConfig( address _vault, address _token, uint256 _tokenDecimals, uint256 _tokenWeight, uint256 _minProfitBps, uint256 _maxUsdgAmount, bool _isStable, bool _isShortable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "vaultSetTokenConfig", _vault, _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable )); _validateAction(action); _clearAction(action); IVault(_vault).setTokenConfig( _token, _tokenDecimals, _tokenWeight, _minProfitBps, _maxUsdgAmount, _isStable, _isShortable ); } function signalPriceFeedSetTokenConfig( address _vaultPriceFeed, address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "priceFeedSetTokenConfig", _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable )); _setPendingAction(action); emit SignalPriceFeedSetTokenConfig( _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable ); } function priceFeedSetTokenConfig( address _vaultPriceFeed, address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked( "priceFeedSetTokenConfig", _vaultPriceFeed, _token, _priceFeed, _priceDecimals, _isStrictStable )); _validateAction(action); _clearAction(action); IVaultPriceFeed(_vaultPriceFeed).setTokenConfig( _token, _priceFeed, _priceDecimals, _isStrictStable ); } function cancelAction(bytes32 _action) external onlyAdmin { _clearAction(_action); } function _mint(address _token, address _receiver, uint256 _amount) private { IMintable mintable = IMintable(_token); if (!mintable.isMinter(address(this))) { mintable.setMinter(address(this), true); } mintable.mint(_receiver, _amount); require(IERC20(_token).totalSupply() <= maxTokenSupply, "GmxTimelock: maxTokenSupply exceeded"); } function _setPendingAction(bytes32 _action) private { pendingActions[_action] = block.timestamp.add(buffer); emit SignalPendingAction(_action); } function _setLongPendingAction(bytes32 _action) private { pendingActions[_action] = block.timestamp.add(longBuffer); emit SignalPendingAction(_action); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action] != 0, "GmxTimelock: action not signalled"); require(pendingActions[_action] < block.timestamp, "GmxTimelock: action time not yet passed"); } function _clearAction(bytes32 _action) private { require(pendingActions[_action] != 0, "GmxTimelock: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IGmxTimelock { function setAdmin(address _admin) external; function setIsLeverageEnabled(address _vault, bool _isLeverageEnabled) external; function signalSetGov(address _target, address _gov) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IRouter { function addPlugin(address _plugin) external; function pluginTransfer(address _token, address _account, address _receiver, uint256 _amount) external; function pluginIncreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external; function pluginDecreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external returns (uint256); function swap(address[] memory _path, uint256 _amountIn, uint256 _minOut, address _receiver) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/Address.sol"; import "../tokens/interfaces/IWETH.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IRouter.sol"; contract Router is IRouter { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address payable; address public gov; // wrapped BNB / ETH address public weth; address public usdg; address public vault; mapping (address => bool) public plugins; mapping (address => mapping (address => bool)) public approvedPlugins; event Swap(address account, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut); modifier onlyGov() { require(msg.sender == gov, "Router: forbidden"); _; } constructor(address _vault, address _usdg, address _weth) public { vault = _vault; usdg = _usdg; weth = _weth; gov = msg.sender; } receive() external payable { require(msg.sender == weth, "Router: invalid sender"); } function setGov(address _gov) external onlyGov { gov = _gov; } function addPlugin(address _plugin) external override onlyGov { plugins[_plugin] = true; } function removePlugin(address _plugin) external onlyGov { plugins[_plugin] = false; } function approvePlugin(address _plugin) external { approvedPlugins[msg.sender][_plugin] = true; } function denyPlugin(address _plugin) external { approvedPlugins[msg.sender][_plugin] = false; } function pluginTransfer(address _token, address _account, address _receiver, uint256 _amount) external override { _validatePlugin(_account); IERC20(_token).safeTransferFrom(_account, _receiver, _amount); } function pluginIncreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong) external override { _validatePlugin(_account); IVault(vault).increasePosition(_account, _collateralToken, _indexToken, _sizeDelta, _isLong); } function pluginDecreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver) external override returns (uint256) { _validatePlugin(_account); return IVault(vault).decreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver); } function directPoolDeposit(address _token, uint256 _amount) external { IERC20(_token).safeTransferFrom(_sender(), vault, _amount); IVault(vault).directPoolDeposit(_token); } function swap(address[] memory _path, uint256 _amountIn, uint256 _minOut, address _receiver) public override { IERC20(_path[0]).safeTransferFrom(_sender(), vault, _amountIn); uint256 amountOut = _swap(_path, _minOut, _receiver); emit Swap(msg.sender, _path[0], _path[_path.length - 1], _amountIn, amountOut); } function swapETHToTokens(address[] memory _path, uint256 _minOut, address _receiver) external payable { require(_path[0] == weth, "Router: invalid _path"); _transferETHToVault(); uint256 amountOut = _swap(_path, _minOut, _receiver); emit Swap(msg.sender, _path[0], _path[_path.length - 1], msg.value, amountOut); } function swapTokensToETH(address[] memory _path, uint256 _amountIn, uint256 _minOut, address payable _receiver) external { require(_path[_path.length - 1] == weth, "Router: invalid _path"); IERC20(_path[0]).safeTransferFrom(_sender(), vault, _amountIn); uint256 amountOut = _swap(_path, _minOut, address(this)); _transferOutETH(amountOut, _receiver); emit Swap(msg.sender, _path[0], _path[_path.length - 1], _amountIn, amountOut); } function increasePosition(address[] memory _path, address _indexToken, uint256 _amountIn, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _price) external { if (_amountIn > 0) { IERC20(_path[0]).safeTransferFrom(_sender(), vault, _amountIn); } if (_path.length > 1 && _amountIn > 0) { uint256 amountOut = _swap(_path, _minOut, address(this)); IERC20(_path[_path.length - 1]).safeTransfer(vault, amountOut); } _increasePosition(_path[_path.length - 1], _indexToken, _sizeDelta, _isLong, _price); } function increasePositionETH(address[] memory _path, address _indexToken, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _price) external payable { require(_path[0] == weth, "Router: invalid _path"); if (msg.value > 0) { _transferETHToVault(); } if (_path.length > 1 && msg.value > 0) { uint256 amountOut = _swap(_path, _minOut, address(this)); IERC20(_path[_path.length - 1]).safeTransfer(vault, amountOut); } _increasePosition(_path[_path.length - 1], _indexToken, _sizeDelta, _isLong, _price); } function decreasePosition(address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price) external { _decreasePosition(_collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver, _price); } function decreasePositionETH(address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address payable _receiver, uint256 _price) external { uint256 amountOut = _decreasePosition(_collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); _transferOutETH(amountOut, _receiver); } function decreasePositionAndSwap(address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price, uint256 _minOut) external { uint256 amount = _decreasePosition(_path[0], _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); IERC20(_path[0]).safeTransfer(vault, amount); _swap(_path, _minOut, _receiver); } function decreasePositionAndSwapETH(address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address payable _receiver, uint256 _price, uint256 _minOut) external { require(_path[_path.length - 1] == weth, "Router: invalid _path"); uint256 amount = _decreasePosition(_path[0], _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); IERC20(_path[0]).safeTransfer(vault, amount); uint256 amountOut = _swap(_path, _minOut, address(this)); _transferOutETH(amountOut, _receiver); } function _increasePosition(address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _price) private { if (_isLong) { require(IVault(vault).getMaxPrice(_indexToken) <= _price, "Router: mark price higher than limit"); } else { require(IVault(vault).getMinPrice(_indexToken) >= _price, "Router: mark price lower than limit"); } IVault(vault).increasePosition(_sender(), _collateralToken, _indexToken, _sizeDelta, _isLong); } function _decreasePosition(address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price) private returns (uint256) { if (_isLong) { require(IVault(vault).getMinPrice(_indexToken) >= _price, "Router: mark price lower than limit"); } else { require(IVault(vault).getMaxPrice(_indexToken) <= _price, "Router: mark price higher than limit"); } return IVault(vault).decreasePosition(_sender(), _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver); } function _transferETHToVault() private { IWETH(weth).deposit{value: msg.value}(); IERC20(weth).safeTransfer(vault, msg.value); } function _transferOutETH(uint256 _amountOut, address payable _receiver) private { IWETH(weth).withdraw(_amountOut); _receiver.sendValue(_amountOut); } function _swap(address[] memory _path, uint256 _minOut, address _receiver) private returns (uint256) { if (_path.length == 2) { return _vaultSwap(_path[0], _path[1], _minOut, _receiver); } if (_path.length == 3) { uint256 midOut = _vaultSwap(_path[0], _path[1], 0, address(this)); IERC20(_path[1]).safeTransfer(vault, midOut); return _vaultSwap(_path[1], _path[2], _minOut, _receiver); } revert("Router: invalid _path.length"); } function _vaultSwap(address _tokenIn, address _tokenOut, uint256 _minOut, address _receiver) private returns (uint256) { uint256 amountOut; if (_tokenOut == usdg) { // buyUSDG amountOut = IVault(vault).buyUSDG(_tokenIn, _receiver); } else if (_tokenIn == usdg) { // sellUSDG amountOut = IVault(vault).sellUSDG(_tokenOut, _receiver); } else { // swap amountOut = IVault(vault).swap(_tokenIn, _tokenOut, _receiver); } require(amountOut >= _minOut, "Router: insufficient amountOut"); return amountOut; } function _sender() private view returns (address) { return msg.sender; } function _validatePlugin(address _account) private view { require(plugins[msg.sender], "Router: invalid plugin"); require(approvedPlugins[_account][msg.sender], "Router: plugin not approved"); } }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IWETH { function deposit() external payable; function transfer(address to, uint value) external returns (bool); function withdraw(uint) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../core/interfaces/IVault.sol"; import "../core/interfaces/IVaultPriceFeed.sol"; import "../core/interfaces/IBasePositionManager.sol"; contract VaultReader { function getVaultTokenInfoV3(address _vault, address _positionManager, address _weth, uint256 _usdgAmount, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 14; IVault vault = IVault(_vault); IVaultPriceFeed priceFeed = IVaultPriceFeed(vault.priceFeed()); IBasePositionManager positionManager = IBasePositionManager(_positionManager); uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } amounts[i * propsLength] = vault.poolAmounts(token); amounts[i * propsLength + 1] = vault.reservedAmounts(token); amounts[i * propsLength + 2] = vault.usdgAmounts(token); amounts[i * propsLength + 3] = vault.getRedemptionAmount(token, _usdgAmount); amounts[i * propsLength + 4] = vault.tokenWeights(token); amounts[i * propsLength + 5] = vault.bufferAmounts(token); amounts[i * propsLength + 6] = vault.maxUsdgAmounts(token); amounts[i * propsLength + 7] = vault.globalShortSizes(token); amounts[i * propsLength + 8] = positionManager.maxGlobalShortSizes(token); amounts[i * propsLength + 9] = vault.getMinPrice(token); amounts[i * propsLength + 10] = vault.getMaxPrice(token); amounts[i * propsLength + 11] = vault.guaranteedUsd(token); amounts[i * propsLength + 12] = priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 13] = priceFeed.getPrimaryPrice(token, true); } return amounts; } function getVaultTokenInfoV4(address _vault, address _positionManager, address _weth, uint256 _usdgAmount, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 15; IVault vault = IVault(_vault); IVaultPriceFeed priceFeed = IVaultPriceFeed(vault.priceFeed()); IBasePositionManager positionManager = IBasePositionManager(_positionManager); uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } amounts[i * propsLength] = vault.poolAmounts(token); amounts[i * propsLength + 1] = vault.reservedAmounts(token); amounts[i * propsLength + 2] = vault.usdgAmounts(token); amounts[i * propsLength + 3] = vault.getRedemptionAmount(token, _usdgAmount); amounts[i * propsLength + 4] = vault.tokenWeights(token); amounts[i * propsLength + 5] = vault.bufferAmounts(token); amounts[i * propsLength + 6] = vault.maxUsdgAmounts(token); amounts[i * propsLength + 7] = vault.globalShortSizes(token); amounts[i * propsLength + 8] = positionManager.maxGlobalShortSizes(token); amounts[i * propsLength + 9] = positionManager.maxGlobalLongSizes(token); amounts[i * propsLength + 10] = vault.getMinPrice(token); amounts[i * propsLength + 11] = vault.getMaxPrice(token); amounts[i * propsLength + 12] = vault.guaranteedUsd(token); amounts[i * propsLength + 13] = priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 14] = priceFeed.getPrimaryPrice(token, true); } return amounts; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IBasePositionManager { function maxGlobalLongSizes(address _token) external view returns (uint256); function maxGlobalShortSizes(address _token) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../tokens/interfaces/IWETH.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/Address.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRouter.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IShortsTracker.sol"; import "./interfaces/IOrderBook.sol"; import "./interfaces/IBasePositionManager.sol"; import "../access/Governable.sol"; import "../peripherals/interfaces/ITimelock.sol"; import "../referrals/interfaces/IReferralStorage.sol"; import "./PositionUtils.sol"; contract BasePositionManager is IBasePositionManager, ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address payable; uint256 public constant BASIS_POINTS_DIVISOR = 10000; address public admin; address public vault; address public shortsTracker; address public router; address public weth; uint256 public ethTransferGasLimit = 500 * 1000; // to prevent using the deposit and withdrawal of collateral as a zero fee swap, // there is a small depositFee charged if a collateral deposit results in the decrease // of leverage for an existing position // increasePositionBufferBps allows for a small amount of decrease of leverage uint256 public depositFee; uint256 public increasePositionBufferBps = 100; address public referralStorage; mapping (address => uint256) public feeReserves; mapping (address => uint256) public override maxGlobalLongSizes; mapping (address => uint256) public override maxGlobalShortSizes; event SetDepositFee(uint256 depositFee); event SetEthTransferGasLimit(uint256 ethTransferGasLimit); event SetIncreasePositionBufferBps(uint256 increasePositionBufferBps); event SetReferralStorage(address referralStorage); event SetAdmin(address admin); event WithdrawFees(address token, address receiver, uint256 amount); event SetMaxGlobalSizes( address[] tokens, uint256[] longSizes, uint256[] shortSizes ); event IncreasePositionReferral( address account, uint256 sizeDelta, uint256 marginFeeBasisPoints, bytes32 referralCode, address referrer ); event DecreasePositionReferral( address account, uint256 sizeDelta, uint256 marginFeeBasisPoints, bytes32 referralCode, address referrer ); modifier onlyAdmin() { require(msg.sender == admin, "forbidden"); _; } constructor( address _vault, address _router, address _shortsTracker, address _weth, uint256 _depositFee ) public { vault = _vault; router = _router; weth = _weth; depositFee = _depositFee; shortsTracker = _shortsTracker; admin = msg.sender; } receive() external payable { require(msg.sender == weth, "invalid sender"); } function setAdmin(address _admin) external onlyGov { admin = _admin; emit SetAdmin(_admin); } function setEthTransferGasLimit(uint256 _ethTransferGasLimit) external onlyAdmin { ethTransferGasLimit = _ethTransferGasLimit; emit SetEthTransferGasLimit(_ethTransferGasLimit); } function setDepositFee(uint256 _depositFee) external onlyAdmin { depositFee = _depositFee; emit SetDepositFee(_depositFee); } function setIncreasePositionBufferBps(uint256 _increasePositionBufferBps) external onlyAdmin { increasePositionBufferBps = _increasePositionBufferBps; emit SetIncreasePositionBufferBps(_increasePositionBufferBps); } function setReferralStorage(address _referralStorage) external onlyAdmin { referralStorage = _referralStorage; emit SetReferralStorage(_referralStorage); } function setMaxGlobalSizes( address[] memory _tokens, uint256[] memory _longSizes, uint256[] memory _shortSizes ) external onlyAdmin { for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; maxGlobalLongSizes[token] = _longSizes[i]; maxGlobalShortSizes[token] = _shortSizes[i]; } emit SetMaxGlobalSizes(_tokens, _longSizes, _shortSizes); } function withdrawFees(address _token, address _receiver) external onlyAdmin { uint256 amount = feeReserves[_token]; if (amount == 0) { return; } feeReserves[_token] = 0; IERC20(_token).safeTransfer(_receiver, amount); emit WithdrawFees(_token, _receiver, amount); } function approve(address _token, address _spender, uint256 _amount) external onlyGov { IERC20(_token).approve(_spender, _amount); } function sendValue(address payable _receiver, uint256 _amount) external onlyGov { _receiver.sendValue(_amount); } function _validateMaxGlobalSize(address _indexToken, bool _isLong, uint256 _sizeDelta) internal view { if (_sizeDelta == 0) { return; } if (_isLong) { uint256 maxGlobalLongSize = maxGlobalLongSizes[_indexToken]; if (maxGlobalLongSize > 0 && IVault(vault).guaranteedUsd(_indexToken).add(_sizeDelta) > maxGlobalLongSize) { revert("max longs exceeded"); } } else { uint256 maxGlobalShortSize = maxGlobalShortSizes[_indexToken]; if (maxGlobalShortSize > 0 && IVault(vault).globalShortSizes(_indexToken).add(_sizeDelta) > maxGlobalShortSize) { revert("max shorts exceeded"); } } } function _increasePosition(address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _price) internal { _validateMaxGlobalSize(_indexToken, _isLong, _sizeDelta); PositionUtils.increasePosition( vault, router, shortsTracker, _account, _collateralToken, _indexToken, _sizeDelta, _isLong, _price ); _emitIncreasePositionReferral(_account, _sizeDelta); } function _decreasePosition(address _account, address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price) internal returns (uint256) { address _vault = vault; uint256 markPrice = _isLong ? IVault(_vault).getMinPrice(_indexToken) : IVault(_vault).getMaxPrice(_indexToken); if (_isLong) { require(markPrice >= _price, "markPrice < price"); } else { require(markPrice <= _price, "markPrice > price"); } address timelock = IVault(_vault).gov(); // should be called strictly before position is updated in Vault IShortsTracker(shortsTracker).updateGlobalShortData(_account, _collateralToken, _indexToken, _isLong, _sizeDelta, markPrice, false); ITimelock(timelock).enableLeverage(_vault); uint256 amountOut = IRouter(router).pluginDecreasePosition(_account, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver); ITimelock(timelock).disableLeverage(_vault); _emitDecreasePositionReferral( _account, _sizeDelta ); return amountOut; } function _swap(address[] memory _path, uint256 _minOut, address _receiver) internal returns (uint256) { if (_path.length == 2) { return _vaultSwap(_path[0], _path[1], _minOut, _receiver); } revert("invalid _path.length"); } function _vaultSwap(address _tokenIn, address _tokenOut, uint256 _minOut, address _receiver) internal returns (uint256) { uint256 amountOut = IVault(vault).swap(_tokenIn, _tokenOut, _receiver); require(amountOut >= _minOut, "insufficient amountOut"); return amountOut; } function _transferInETH() internal { if (msg.value != 0) { IWETH(weth).deposit{value: msg.value}(); } } function _transferOutETHWithGasLimitFallbackToWeth(uint256 _amountOut, address payable _receiver) internal { IWETH _weth = IWETH(weth); _weth.withdraw(_amountOut); (bool success, /* bytes memory data */) = _receiver.call{ value: _amountOut, gas: ethTransferGasLimit }(""); if (success) { return; } // if the transfer failed, re-wrap the token and send it to the receiver _weth.deposit{ value: _amountOut }(); _weth.transfer(address(_receiver), _amountOut); } function _collectFees( address _account, address[] memory _path, uint256 _amountIn, address _indexToken, bool _isLong, uint256 _sizeDelta ) internal returns (uint256) { bool shouldDeductFee = PositionUtils.shouldDeductFee( vault, _account, _path, _amountIn, _indexToken, _isLong, _sizeDelta, increasePositionBufferBps ); if (shouldDeductFee) { uint256 afterFeeAmount = _amountIn.mul(BASIS_POINTS_DIVISOR.sub(depositFee)).div(BASIS_POINTS_DIVISOR); uint256 feeAmount = _amountIn.sub(afterFeeAmount); address feeToken = _path[_path.length - 1]; feeReserves[feeToken] = feeReserves[feeToken].add(feeAmount); return afterFeeAmount; } return _amountIn; } function _emitIncreasePositionReferral(address _account, uint256 _sizeDelta) internal { address _referralStorage = referralStorage; if (_referralStorage == address(0)) { return; } (bytes32 referralCode, address referrer) = IReferralStorage(_referralStorage).getTraderReferralInfo(_account); if (referralCode == bytes32(0)) { return; } address timelock = IVault(vault).gov(); emit IncreasePositionReferral( _account, _sizeDelta, ITimelock(timelock).marginFeeBasisPoints(), referralCode, referrer ); } function _emitDecreasePositionReferral(address _account, uint256 _sizeDelta) internal { address _referralStorage = referralStorage; if (_referralStorage == address(0)) { return; } (bytes32 referralCode, address referrer) = IReferralStorage(_referralStorage).getTraderReferralInfo(_account); if (referralCode == bytes32(0)) { return; } address timelock = IVault(vault).gov(); emit DecreasePositionReferral( _account, _sizeDelta, ITimelock(timelock).marginFeeBasisPoints(), referralCode, referrer ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IShortsTracker { function isGlobalShortDataReady() external view returns (bool); function globalShortAveragePrices(address _token) external view returns (uint256); function getNextGlobalShortData( address _account, address _collateralToken, address _indexToken, uint256 _nextPrice, uint256 _sizeDelta, bool _isIncrease ) external view returns (uint256, uint256); function updateGlobalShortData( address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta, uint256 _markPrice, bool _isIncrease ) external; function setIsGlobalShortDataReady(bool value) external; function setInitData(address[] calldata _tokens, uint256[] calldata _averagePrices) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IOrderBook { function getSwapOrder(address _account, uint256 _orderIndex) external view returns ( address path0, address path1, address path2, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ); function getIncreaseOrder(address _account, uint256 _orderIndex) external view returns ( address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); function getDecreaseOrder(address _account, uint256 _orderIndex) external view returns ( address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); function executeSwapOrder(address, uint256, address payable) external; function executeDecreaseOrder(address, uint256, address payable) external; function executeIncreaseOrder(address, uint256, address payable) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../libraries/math/SafeMath.sol"; import "../peripherals/interfaces/ITimelock.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IRouter.sol"; import "./interfaces/IShortsTracker.sol"; library PositionUtils { using SafeMath for uint256; uint256 public constant BASIS_POINTS_DIVISOR = 10000; event LeverageDecreased(uint256 collateralDelta, uint256 prevLeverage, uint256 nextLeverage); function shouldDeductFee( address _vault, address _account, address[] memory _path, uint256 _amountIn, address _indexToken, bool _isLong, uint256 _sizeDelta, uint256 _increasePositionBufferBps ) external returns (bool) { // if the position is a short, do not charge a fee if (!_isLong) { return false; } // if the position size is not increasing, this is a collateral deposit if (_sizeDelta == 0) { return true; } address collateralToken = _path[_path.length - 1]; IVault vault = IVault(_vault); (uint256 size, uint256 collateral, , , , , , ) = vault.getPosition(_account, collateralToken, _indexToken, _isLong); // if there is no existing position, do not charge a fee if (size == 0) { return false; } uint256 nextSize = size.add(_sizeDelta); uint256 collateralDelta = vault.tokenToUsdMin(collateralToken, _amountIn); uint256 nextCollateral = collateral.add(collateralDelta); uint256 prevLeverage = size.mul(BASIS_POINTS_DIVISOR).div(collateral); // allow for a maximum of a increasePositionBufferBps decrease since there might be some swap fees taken from the collateral uint256 nextLeverage = nextSize.mul(BASIS_POINTS_DIVISOR + _increasePositionBufferBps).div(nextCollateral); emit LeverageDecreased(collateralDelta, prevLeverage, nextLeverage); // deduct a fee if the leverage is decreased return nextLeverage < prevLeverage; } function increasePosition( address _vault, address _router, address _shortsTracker, address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _price ) external { uint256 markPrice = _isLong ? IVault(_vault).getMaxPrice(_indexToken) : IVault(_vault).getMinPrice(_indexToken); if (_isLong) { require(markPrice <= _price, "markPrice > price"); } else { require(markPrice >= _price, "markPrice < price"); } address timelock = IVault(_vault).gov(); // should be called strictly before position is updated in Vault IShortsTracker(_shortsTracker).updateGlobalShortData(_account, _collateralToken, _indexToken, _isLong, _sizeDelta, markPrice, true); ITimelock(timelock).enableLeverage(_vault); IRouter(_router).pluginIncreasePosition(_account, _collateralToken, _indexToken, _sizeDelta, _isLong); ITimelock(timelock).disableLeverage(_vault); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "./interfaces/IRouter.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IPositionRouter.sol"; import "./interfaces/IPositionRouterCallbackReceiver.sol"; import "../libraries/utils/Address.sol"; import "../peripherals/interfaces/ITimelock.sol"; import "./BasePositionManager.sol"; contract PositionRouter is BasePositionManager, IPositionRouter { using Address for address; struct IncreasePositionRequest { address account; address[] path; address indexToken; uint256 amountIn; uint256 minOut; uint256 sizeDelta; bool isLong; uint256 acceptablePrice; uint256 executionFee; uint256 blockNumber; uint256 blockTime; bool hasCollateralInETH; address callbackTarget; } struct DecreasePositionRequest { address account; address[] path; address indexToken; uint256 collateralDelta; uint256 sizeDelta; bool isLong; address receiver; uint256 acceptablePrice; uint256 minOut; uint256 executionFee; uint256 blockNumber; uint256 blockTime; bool withdrawETH; address callbackTarget; } uint256 public minExecutionFee; uint256 public minBlockDelayKeeper; uint256 public minTimeDelayPublic; uint256 public maxTimeDelay; bool public isLeverageEnabled = true; bytes32[] public override increasePositionRequestKeys; bytes32[] public override decreasePositionRequestKeys; uint256 public override increasePositionRequestKeysStart; uint256 public override decreasePositionRequestKeysStart; uint256 public callbackGasLimit; mapping (address => uint256) public customCallbackGasLimits; mapping (address => bool) public isPositionKeeper; mapping (address => uint256) public increasePositionsIndex; mapping (bytes32 => IncreasePositionRequest) public increasePositionRequests; mapping (address => uint256) public decreasePositionsIndex; mapping (bytes32 => DecreasePositionRequest) public decreasePositionRequests; event CreateIncreasePosition( address indexed account, address[] path, address indexToken, uint256 amountIn, uint256 minOut, uint256 sizeDelta, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 index, uint256 queueIndex, uint256 blockNumber, uint256 blockTime, uint256 gasPrice ); event ExecuteIncreasePosition( address indexed account, address[] path, address indexToken, uint256 amountIn, uint256 minOut, uint256 sizeDelta, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 blockGap, uint256 timeGap ); event CancelIncreasePosition( address indexed account, address[] path, address indexToken, uint256 amountIn, uint256 minOut, uint256 sizeDelta, bool isLong, uint256 acceptablePrice, uint256 executionFee, uint256 blockGap, uint256 timeGap ); event CreateDecreasePosition( address indexed account, address[] path, address indexToken, uint256 collateralDelta, uint256 sizeDelta, bool isLong, address receiver, uint256 acceptablePrice, uint256 minOut, uint256 executionFee, uint256 index, uint256 queueIndex, uint256 blockNumber, uint256 blockTime ); event ExecuteDecreasePosition( address indexed account, address[] path, address indexToken, uint256 collateralDelta, uint256 sizeDelta, bool isLong, address receiver, uint256 acceptablePrice, uint256 minOut, uint256 executionFee, uint256 blockGap, uint256 timeGap ); event CancelDecreasePosition( address indexed account, address[] path, address indexToken, uint256 collateralDelta, uint256 sizeDelta, bool isLong, address receiver, uint256 acceptablePrice, uint256 minOut, uint256 executionFee, uint256 blockGap, uint256 timeGap ); event SetPositionKeeper(address indexed account, bool isActive); event SetMinExecutionFee(uint256 minExecutionFee); event SetIsLeverageEnabled(bool isLeverageEnabled); event SetDelayValues(uint256 minBlockDelayKeeper, uint256 minTimeDelayPublic, uint256 maxTimeDelay); event SetRequestKeysStartValues(uint256 increasePositionRequestKeysStart, uint256 decreasePositionRequestKeysStart); event SetCallbackGasLimit(uint256 callbackGasLimit); event SetCustomCallbackGasLimit(address callbackTarget, uint256 callbackGasLimit); event Callback(address callbackTarget, bool success, uint256 callbackGasLimit); modifier onlyPositionKeeper() { require(isPositionKeeper[msg.sender], "403"); _; } constructor( address _vault, address _router, address _weth, address _shortsTracker, uint256 _depositFee, uint256 _minExecutionFee ) public BasePositionManager(_vault, _router, _shortsTracker, _weth, _depositFee) { minExecutionFee = _minExecutionFee; } function setPositionKeeper(address _account, bool _isActive) external onlyAdmin { isPositionKeeper[_account] = _isActive; emit SetPositionKeeper(_account, _isActive); } function setCallbackGasLimit(uint256 _callbackGasLimit) external onlyAdmin { callbackGasLimit = _callbackGasLimit; emit SetCallbackGasLimit(_callbackGasLimit); } function setCustomCallbackGasLimit(address _callbackTarget, uint256 _callbackGasLimit) external onlyAdmin { customCallbackGasLimits[_callbackTarget] = _callbackGasLimit; emit SetCustomCallbackGasLimit(_callbackTarget, _callbackGasLimit); } function setMinExecutionFee(uint256 _minExecutionFee) external onlyAdmin { minExecutionFee = _minExecutionFee; emit SetMinExecutionFee(_minExecutionFee); } function setIsLeverageEnabled(bool _isLeverageEnabled) external onlyAdmin { isLeverageEnabled = _isLeverageEnabled; emit SetIsLeverageEnabled(_isLeverageEnabled); } function setDelayValues(uint256 _minBlockDelayKeeper, uint256 _minTimeDelayPublic, uint256 _maxTimeDelay) external onlyAdmin { minBlockDelayKeeper = _minBlockDelayKeeper; minTimeDelayPublic = _minTimeDelayPublic; maxTimeDelay = _maxTimeDelay; emit SetDelayValues(_minBlockDelayKeeper, _minTimeDelayPublic, _maxTimeDelay); } function setRequestKeysStartValues(uint256 _increasePositionRequestKeysStart, uint256 _decreasePositionRequestKeysStart) external onlyAdmin { increasePositionRequestKeysStart = _increasePositionRequestKeysStart; decreasePositionRequestKeysStart = _decreasePositionRequestKeysStart; emit SetRequestKeysStartValues(_increasePositionRequestKeysStart, _decreasePositionRequestKeysStart); } function executeIncreasePositions(uint256 _endIndex, address payable _executionFeeReceiver) external override onlyPositionKeeper { uint256 index = increasePositionRequestKeysStart; uint256 length = increasePositionRequestKeys.length; if (index >= length) { return; } if (_endIndex > length) { _endIndex = length; } while (index < _endIndex) { bytes32 key = increasePositionRequestKeys[index]; // if the request was executed then delete the key from the array // if the request was not executed then break from the loop, this can happen if the // minimum number of blocks has not yet passed // an error could be thrown if the request is too old or if the slippage is // higher than what the user specified, or if there is insufficient liquidity for the position // in case an error was thrown, cancel the request try this.executeIncreasePosition(key, _executionFeeReceiver) returns (bool _wasExecuted) { if (!_wasExecuted) { break; } } catch { // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelIncreasePosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } delete increasePositionRequestKeys[index]; index++; } increasePositionRequestKeysStart = index; } function executeDecreasePositions(uint256 _endIndex, address payable _executionFeeReceiver) external override onlyPositionKeeper { uint256 index = decreasePositionRequestKeysStart; uint256 length = decreasePositionRequestKeys.length; if (index >= length) { return; } if (_endIndex > length) { _endIndex = length; } while (index < _endIndex) { bytes32 key = decreasePositionRequestKeys[index]; // if the request was executed then delete the key from the array // if the request was not executed then break from the loop, this can happen if the // minimum number of blocks has not yet passed // an error could be thrown if the request is too old // in case an error was thrown, cancel the request try this.executeDecreasePosition(key, _executionFeeReceiver) returns (bool _wasExecuted) { if (!_wasExecuted) { break; } } catch { // wrap this call in a try catch to prevent invalid cancels from blocking the loop try this.cancelDecreasePosition(key, _executionFeeReceiver) returns (bool _wasCancelled) { if (!_wasCancelled) { break; } } catch {} } delete decreasePositionRequestKeys[index]; index++; } decreasePositionRequestKeysStart = index; } function createIncreasePosition( address[] memory _path, address _indexToken, uint256 _amountIn, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee, bytes32 _referralCode, address _callbackTarget ) external payable nonReentrant returns (bytes32) { require(_executionFee >= minExecutionFee, "fee"); require(msg.value == _executionFee, "val"); require(_path.length == 1 || _path.length == 2, "len"); _transferInETH(); _setTraderReferralCode(_referralCode); if (_amountIn > 0) { IRouter(router).pluginTransfer(_path[0], msg.sender, address(this), _amountIn); } return _createIncreasePosition( msg.sender, _path, _indexToken, _amountIn, _minOut, _sizeDelta, _isLong, _acceptablePrice, _executionFee, false, _callbackTarget ); } function createIncreasePositionETH( address[] memory _path, address _indexToken, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee, bytes32 _referralCode, address _callbackTarget ) external payable nonReentrant returns (bytes32) { require(_executionFee >= minExecutionFee, "fee"); require(msg.value >= _executionFee, "val"); require(_path.length == 1 || _path.length == 2, "len"); require(_path[0] == weth, "path"); _transferInETH(); _setTraderReferralCode(_referralCode); uint256 amountIn = msg.value.sub(_executionFee); return _createIncreasePosition( msg.sender, _path, _indexToken, amountIn, _minOut, _sizeDelta, _isLong, _acceptablePrice, _executionFee, true, _callbackTarget ); } function createDecreasePosition( address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _acceptablePrice, uint256 _minOut, uint256 _executionFee, bool _withdrawETH, address _callbackTarget ) external payable nonReentrant returns (bytes32) { require(_executionFee >= minExecutionFee, "fee"); require(msg.value == _executionFee, "val"); require(_path.length == 1 || _path.length == 2, "len"); if (_withdrawETH) { require(_path[_path.length - 1] == weth, "path"); } _transferInETH(); return _createDecreasePosition( msg.sender, _path, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver, _acceptablePrice, _minOut, _executionFee, _withdrawETH, _callbackTarget ); } function getRequestQueueLengths() external view override returns (uint256, uint256, uint256, uint256) { return ( increasePositionRequestKeysStart, increasePositionRequestKeys.length, decreasePositionRequestKeysStart, decreasePositionRequestKeys.length ); } function executeIncreasePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { IncreasePositionRequest memory request = increasePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeIncreasePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldExecute = _validateExecution(request.blockNumber, request.blockTime, request.account); if (!shouldExecute) { return false; } delete increasePositionRequests[_key]; if (request.amountIn > 0) { uint256 amountIn = request.amountIn; if (request.path.length > 1) { IERC20(request.path[0]).safeTransfer(vault, request.amountIn); amountIn = _swap(request.path, request.minOut, address(this)); } uint256 afterFeeAmount = _collectFees(request.account, request.path, amountIn, request.indexToken, request.isLong, request.sizeDelta); IERC20(request.path[request.path.length - 1]).safeTransfer(vault, afterFeeAmount); } _increasePosition(request.account, request.path[request.path.length - 1], request.indexToken, request.sizeDelta, request.isLong, request.acceptablePrice); _transferOutETHWithGasLimitFallbackToWeth(request.executionFee, _executionFeeReceiver); emit ExecuteIncreasePosition( request.account, request.path, request.indexToken, request.amountIn, request.minOut, request.sizeDelta, request.isLong, request.acceptablePrice, request.executionFee, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); _callRequestCallback(request.callbackTarget, _key, true, true); return true; } function cancelIncreasePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { IncreasePositionRequest memory request = increasePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeIncreasePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldCancel = _validateCancellation(request.blockNumber, request.blockTime, request.account); if (!shouldCancel) { return false; } delete increasePositionRequests[_key]; if (request.hasCollateralInETH) { _transferOutETHWithGasLimitFallbackToWeth(request.amountIn, payable(request.account)); } else { IERC20(request.path[0]).safeTransfer(request.account, request.amountIn); } _transferOutETHWithGasLimitFallbackToWeth(request.executionFee, _executionFeeReceiver); emit CancelIncreasePosition( request.account, request.path, request.indexToken, request.amountIn, request.minOut, request.sizeDelta, request.isLong, request.acceptablePrice, request.executionFee, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); _callRequestCallback(request.callbackTarget, _key, false, true); return true; } function executeDecreasePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { DecreasePositionRequest memory request = decreasePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeDecreasePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldExecute = _validateExecution(request.blockNumber, request.blockTime, request.account); if (!shouldExecute) { return false; } delete decreasePositionRequests[_key]; uint256 amountOut = _decreasePosition(request.account, request.path[0], request.indexToken, request.collateralDelta, request.sizeDelta, request.isLong, address(this), request.acceptablePrice); if (amountOut > 0) { if (request.path.length > 1) { IERC20(request.path[0]).safeTransfer(vault, amountOut); amountOut = _swap(request.path, request.minOut, address(this)); } if (request.withdrawETH) { _transferOutETHWithGasLimitFallbackToWeth(amountOut, payable(request.receiver)); } else { IERC20(request.path[request.path.length - 1]).safeTransfer(request.receiver, amountOut); } } _transferOutETHWithGasLimitFallbackToWeth(request.executionFee, _executionFeeReceiver); emit ExecuteDecreasePosition( request.account, request.path, request.indexToken, request.collateralDelta, request.sizeDelta, request.isLong, request.receiver, request.acceptablePrice, request.minOut, request.executionFee, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); _callRequestCallback(request.callbackTarget, _key, true, false); return true; } function cancelDecreasePosition(bytes32 _key, address payable _executionFeeReceiver) public nonReentrant returns (bool) { DecreasePositionRequest memory request = decreasePositionRequests[_key]; // if the request was already executed or cancelled, return true so that the executeDecreasePositions loop will continue executing the next request if (request.account == address(0)) { return true; } bool shouldCancel = _validateCancellation(request.blockNumber, request.blockTime, request.account); if (!shouldCancel) { return false; } delete decreasePositionRequests[_key]; _transferOutETHWithGasLimitFallbackToWeth(request.executionFee, _executionFeeReceiver); emit CancelDecreasePosition( request.account, request.path, request.indexToken, request.collateralDelta, request.sizeDelta, request.isLong, request.receiver, request.acceptablePrice, request.minOut, request.executionFee, block.number.sub(request.blockNumber), block.timestamp.sub(request.blockTime) ); _callRequestCallback(request.callbackTarget, _key, false, false); return true; } function getRequestKey(address _account, uint256 _index) public pure returns (bytes32) { return keccak256(abi.encodePacked(_account, _index)); } function getIncreasePositionRequestPath(bytes32 _key) public view override returns (address[] memory) { IncreasePositionRequest memory request = increasePositionRequests[_key]; return request.path; } function getDecreasePositionRequestPath(bytes32 _key) public view override returns (address[] memory) { DecreasePositionRequest memory request = decreasePositionRequests[_key]; return request.path; } function _setTraderReferralCode(bytes32 _referralCode) internal { if (_referralCode != bytes32(0) && referralStorage != address(0)) { IReferralStorage(referralStorage).setTraderReferralCode(msg.sender, _referralCode); } } function _validateExecution(uint256 _positionBlockNumber, uint256 _positionBlockTime, address _account) internal view returns (bool) { if (_positionBlockTime.add(maxTimeDelay) <= block.timestamp) { revert("expired"); } return _validateExecutionOrCancellation(_positionBlockNumber, _positionBlockTime, _account); } function _validateCancellation(uint256 _positionBlockNumber, uint256 _positionBlockTime, address _account) internal view returns (bool) { return _validateExecutionOrCancellation(_positionBlockNumber, _positionBlockTime, _account); } function _validateExecutionOrCancellation(uint256 _positionBlockNumber, uint256 _positionBlockTime, address _account) internal view returns (bool) { bool isKeeperCall = msg.sender == address(this) || isPositionKeeper[msg.sender]; if (!isLeverageEnabled && !isKeeperCall) { revert("403"); } if (isKeeperCall) { return _positionBlockNumber.add(minBlockDelayKeeper) <= block.number; } require(msg.sender == _account, "403"); require(_positionBlockTime.add(minTimeDelayPublic) <= block.timestamp, "delay"); return true; } function _createIncreasePosition( address _account, address[] memory _path, address _indexToken, uint256 _amountIn, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _acceptablePrice, uint256 _executionFee, bool _hasCollateralInETH, address _callbackTarget ) internal returns (bytes32) { IncreasePositionRequest memory request = IncreasePositionRequest( _account, _path, _indexToken, _amountIn, _minOut, _sizeDelta, _isLong, _acceptablePrice, _executionFee, block.number, block.timestamp, _hasCollateralInETH, _callbackTarget ); (uint256 index, bytes32 requestKey) = _storeIncreasePositionRequest(request); emit CreateIncreasePosition( _account, _path, _indexToken, _amountIn, _minOut, _sizeDelta, _isLong, _acceptablePrice, _executionFee, index, increasePositionRequestKeys.length - 1, block.number, block.timestamp, tx.gasprice ); return requestKey; } function _storeIncreasePositionRequest(IncreasePositionRequest memory _request) internal returns (uint256, bytes32) { address account = _request.account; uint256 index = increasePositionsIndex[account].add(1); increasePositionsIndex[account] = index; bytes32 key = getRequestKey(account, index); increasePositionRequests[key] = _request; increasePositionRequestKeys.push(key); return (index, key); } function _storeDecreasePositionRequest(DecreasePositionRequest memory _request) internal returns (uint256, bytes32) { address account = _request.account; uint256 index = decreasePositionsIndex[account].add(1); decreasePositionsIndex[account] = index; bytes32 key = getRequestKey(account, index); decreasePositionRequests[key] = _request; decreasePositionRequestKeys.push(key); return (index, key); } function _createDecreasePosition( address _account, address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _acceptablePrice, uint256 _minOut, uint256 _executionFee, bool _withdrawETH, address _callbackTarget ) internal returns (bytes32) { DecreasePositionRequest memory request = DecreasePositionRequest( _account, _path, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver, _acceptablePrice, _minOut, _executionFee, block.number, block.timestamp, _withdrawETH, _callbackTarget ); (uint256 index, bytes32 requestKey) = _storeDecreasePositionRequest(request); emit CreateDecreasePosition( request.account, request.path, request.indexToken, request.collateralDelta, request.sizeDelta, request.isLong, request.receiver, request.acceptablePrice, request.minOut, request.executionFee, index, decreasePositionRequestKeys.length - 1, block.number, block.timestamp ); return requestKey; } function _callRequestCallback( address _callbackTarget, bytes32 _key, bool _wasExecuted, bool _isIncrease ) internal { if (_callbackTarget == address(0)) { return; } if (!_callbackTarget.isContract()) { return; } uint256 _gasLimit = callbackGasLimit; uint256 _customCallbackGasLimit = customCallbackGasLimits[_callbackTarget]; if (_customCallbackGasLimit > _gasLimit) { _gasLimit = _customCallbackGasLimit; } if (_gasLimit == 0) { return; } bool success; try IPositionRouterCallbackReceiver(_callbackTarget).gmxPositionCallback{ gas: _gasLimit }(_key, _wasExecuted, _isIncrease) { success = true; } catch {} emit Callback(_callbackTarget, success, _gasLimit); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPositionRouter { function increasePositionRequestKeysStart() external view returns (uint256); function decreasePositionRequestKeysStart() external view returns (uint256); function increasePositionRequestKeys(uint256 index) external view returns (bytes32); function decreasePositionRequestKeys(uint256 index) external view returns (bytes32); function executeIncreasePositions(uint256 _count, address payable _executionFeeReceiver) external; function executeDecreasePositions(uint256 _count, address payable _executionFeeReceiver) external; function getRequestQueueLengths() external view returns (uint256, uint256, uint256, uint256); function getIncreasePositionRequestPath(bytes32 _key) external view returns (address[] memory); function getDecreasePositionRequestPath(bytes32 _key) external view returns (address[] memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; interface IPositionRouterCallbackReceiver { function gmxPositionCallback(bytes32 positionKey, bool isExecuted, bool isIncrease) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../core/PositionRouter.sol"; contract PositionRouterReader { function getTransferTokenOfIncreasePositionRequests( address _positionRouter, uint256 _endIndex ) external view returns (uint256[] memory, address[] memory) { IPositionRouter positionRouter = IPositionRouter(_positionRouter); // increasePositionRequestKeysStart, // increasePositionRequestKeys.length, // decreasePositionRequestKeysStart, // decreasePositionRequestKeys.length (uint256 index, uint256 length, ,) = positionRouter.getRequestQueueLengths(); if (_endIndex > length) { _endIndex = length; } uint256[] memory requestIndexes = new uint256[](_endIndex - index); address[] memory transferTokens = new address[](_endIndex - index); uint256 transferTokenIndex = 0; while (index < _endIndex) { bytes32 key = positionRouter.increasePositionRequestKeys(index); address[] memory path = positionRouter.getIncreasePositionRequestPath(key); if (path.length > 0) { transferTokens[transferTokenIndex] = path[0]; } requestIndexes[transferTokenIndex] = index; transferTokenIndex++; index++; } return (requestIndexes, transferTokens); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../libraries/math/SafeMath.sol"; import "../access/Governable.sol"; import "../peripherals/interfaces/ITimelock.sol"; import "./interfaces/IReferralStorage.sol"; contract ReferralStorage is Governable, IReferralStorage { using SafeMath for uint256; struct Tier { uint256 totalRebate; // e.g. 2400 for 24% uint256 discountShare; // 5000 for 50%/50%, 7000 for 30% rebates/70% discount } uint256 public constant BASIS_POINTS = 10000; mapping (address => uint256) public override referrerDiscountShares; // to override default value in tier mapping (address => uint256) public override referrerTiers; // link between user <> tier mapping (uint256 => Tier) public tiers; mapping (address => bool) public isHandler; mapping (bytes32 => address) public override codeOwners; mapping (address => bytes32) public override traderReferralCodes; event SetHandler(address handler, bool isActive); event SetTraderReferralCode(address account, bytes32 code); event SetTier(uint256 tierId, uint256 totalRebate, uint256 discountShare); event SetReferrerTier(address referrer, uint256 tierId); event SetReferrerDiscountShare(address referrer, uint256 discountShare); event RegisterCode(address account, bytes32 code); event SetCodeOwner(address account, address newAccount, bytes32 code); event GovSetCodeOwner(bytes32 code, address newAccount); modifier onlyHandler() { require(isHandler[msg.sender], "ReferralStorage: forbidden"); _; } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; emit SetHandler(_handler, _isActive); } function setTier(uint256 _tierId, uint256 _totalRebate, uint256 _discountShare) external override onlyGov { require(_totalRebate <= BASIS_POINTS, "ReferralStorage: invalid totalRebate"); require(_discountShare <= BASIS_POINTS, "ReferralStorage: invalid discountShare"); Tier memory tier = tiers[_tierId]; tier.totalRebate = _totalRebate; tier.discountShare = _discountShare; tiers[_tierId] = tier; emit SetTier(_tierId, _totalRebate, _discountShare); } function setReferrerTier(address _referrer, uint256 _tierId) external override onlyGov { referrerTiers[_referrer] = _tierId; emit SetReferrerTier(_referrer, _tierId); } function setReferrerDiscountShare(uint256 _discountShare) external { require(_discountShare <= BASIS_POINTS, "ReferralStorage: invalid discountShare"); referrerDiscountShares[msg.sender] = _discountShare; emit SetReferrerDiscountShare(msg.sender, _discountShare); } function setTraderReferralCode(address _account, bytes32 _code) external override onlyHandler { _setTraderReferralCode(_account, _code); } function setTraderReferralCodeByUser(bytes32 _code) external { _setTraderReferralCode(msg.sender, _code); } function registerCode(bytes32 _code) external { require(_code != bytes32(0), "ReferralStorage: invalid _code"); require(codeOwners[_code] == address(0), "ReferralStorage: code already exists"); codeOwners[_code] = msg.sender; emit RegisterCode(msg.sender, _code); } function setCodeOwner(bytes32 _code, address _newAccount) external { require(_code != bytes32(0), "ReferralStorage: invalid _code"); address account = codeOwners[_code]; require(msg.sender == account, "ReferralStorage: forbidden"); codeOwners[_code] = _newAccount; emit SetCodeOwner(msg.sender, _newAccount, _code); } function govSetCodeOwner(bytes32 _code, address _newAccount) external override onlyGov { require(_code != bytes32(0), "ReferralStorage: invalid _code"); codeOwners[_code] = _newAccount; emit GovSetCodeOwner(_code, _newAccount); } function getTraderReferralInfo(address _account) external override view returns (bytes32, address) { bytes32 code = traderReferralCodes[_account]; address referrer; if (code != bytes32(0)) { referrer = codeOwners[code]; } return (code, referrer); } function _setTraderReferralCode(address _account, bytes32 _code) private { traderReferralCodes[_account] = _code; emit SetTraderReferralCode(_account, _code); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IReferralStorage.sol"; contract ReferralReader { function getCodeOwners(IReferralStorage _referralStorage, bytes32[] memory _codes) public view returns (address[] memory) { address[] memory owners = new address[](_codes.length); for (uint256 i = 0; i < _codes.length; i++) { bytes32 code = _codes[i]; owners[i] = _referralStorage.codeOwners(code); } return owners; } }
// SPDX-License-Identifier: MIT import "../libraries/math/SafeMath.sol"; import "../access/Governable.sol"; import "../core/interfaces/IShortsTracker.sol"; import "./interfaces/IHandlerTarget.sol"; pragma solidity 0.6.12; contract ShortsTrackerTimelock { using SafeMath for uint256; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant MAX_BUFFER = 5 days; mapping (bytes32 => uint256) public pendingActions; address public admin; uint256 public buffer; mapping (address => bool) public isHandler; mapping (address => uint256) public lastUpdated; uint256 public averagePriceUpdateDelay; uint256 public maxAveragePriceChange; event GlobalShortAveragePriceUpdated(address indexed token, uint256 oldAveragePrice, uint256 newAveragePrice); event SignalSetGov(address target, address gov); event SetGov(address target, address gov); event SignalSetAdmin(address admin); event SetAdmin(address admin); event SetContractHandler(address indexed handler, bool isHandler); event SignalSetHandler(address target, address handler, bool isActive, bytes32 action); event SignalSetMaxAveragePriceChange(uint256 maxAveragePriceChange); event SetMaxAveragePriceChange(uint256 maxAveragePriceChange); event SignalSetAveragePriceUpdateDelay(uint256 averagePriceUpdateDelay); event SetAveragePriceUpdateDelay(uint256 averagePriceUpdateDelay); event SignalSetIsGlobalShortDataReady(address target, bool isGlobalShortDataReady); event SetIsGlobalShortDataReady(address target, bool isGlobalShortDataReady); event SignalPendingAction(bytes32 action); event ClearAction(bytes32 action); constructor( address _admin, uint256 _buffer, uint256 _averagePriceUpdateDelay, uint256 _maxAveragePriceChange ) public { admin = _admin; buffer = _buffer; averagePriceUpdateDelay = _averagePriceUpdateDelay; maxAveragePriceChange = _maxAveragePriceChange; } modifier onlyAdmin() { require(msg.sender == admin, "ShortsTrackerTimelock: admin forbidden"); _; } modifier onlyHandler() { require(isHandler[msg.sender] || msg.sender == admin, "ShortsTrackerTimelock: handler forbidden"); _; } function setBuffer(uint256 _buffer) external onlyAdmin { require(_buffer <= MAX_BUFFER, "ShortsTrackerTimelock: invalid buffer"); require(_buffer > buffer, "ShortsTrackerTimelock: buffer cannot be decreased"); buffer = _buffer; } function signalSetAdmin(address _admin) external onlyAdmin { require(_admin != address(0), "ShortsTrackerTimelock: invalid admin"); bytes32 action = keccak256(abi.encodePacked("setAdmin", _admin)); _setPendingAction(action); emit SignalSetAdmin(_admin); } function setAdmin(address _admin) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setAdmin", _admin)); _validateAction(action); _clearAction(action); admin = _admin; emit SetAdmin(_admin); } function setContractHandler(address _handler, bool _isActive) external onlyAdmin { isHandler[_handler] = _isActive; emit SetContractHandler(_handler, _isActive); } function signalSetGov(address _shortsTracker, address _gov) external onlyAdmin { require(_gov != address(0), "ShortsTrackerTimelock: invalid gov"); bytes32 action = keccak256(abi.encodePacked("setGov", _shortsTracker, _gov)); _setPendingAction(action); emit SignalSetGov(_shortsTracker, _gov); } function setGov(address _shortsTracker, address _gov) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setGov", _shortsTracker, _gov)); _validateAction(action); _clearAction(action); Governable(_shortsTracker).setGov(_gov); emit SetGov(_shortsTracker, _gov); } function signalSetHandler(address _target, address _handler, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive)); _setPendingAction(action); emit SignalSetHandler(_target, _handler, _isActive, action); } function setHandler(address _target, address _handler, bool _isActive) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive)); _validateAction(action); _clearAction(action); IHandlerTarget(_target).setHandler(_handler, _isActive); } function signalSetAveragePriceUpdateDelay(uint256 _averagePriceUpdateDelay) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setAveragePriceUpdateDelay", _averagePriceUpdateDelay)); _setPendingAction(action); emit SignalSetAveragePriceUpdateDelay(_averagePriceUpdateDelay); } function setAveragePriceUpdateDelay(uint256 _averagePriceUpdateDelay) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setAveragePriceUpdateDelay", _averagePriceUpdateDelay)); _validateAction(action); _clearAction(action); averagePriceUpdateDelay = _averagePriceUpdateDelay; emit SetAveragePriceUpdateDelay(_averagePriceUpdateDelay); } function signalSetMaxAveragePriceChange(uint256 _maxAveragePriceChange) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setMaxAveragePriceChange", _maxAveragePriceChange)); _setPendingAction(action); emit SignalSetMaxAveragePriceChange(_maxAveragePriceChange); } function setMaxAveragePriceChange(uint256 _maxAveragePriceChange) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setMaxAveragePriceChange", _maxAveragePriceChange)); _validateAction(action); _clearAction(action); maxAveragePriceChange = _maxAveragePriceChange; emit SetMaxAveragePriceChange(_maxAveragePriceChange); } function signalSetIsGlobalShortDataReady(IShortsTracker _shortsTracker, bool _value) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setIsGlobalShortDataReady", address(_shortsTracker), _value)); _setPendingAction(action); emit SignalSetIsGlobalShortDataReady(address(_shortsTracker), _value); } function setIsGlobalShortDataReady(IShortsTracker _shortsTracker, bool _value) external onlyAdmin { bytes32 action = keccak256(abi.encodePacked("setIsGlobalShortDataReady", address(_shortsTracker), _value)); _validateAction(action); _clearAction(action); _shortsTracker.setIsGlobalShortDataReady(_value); emit SetIsGlobalShortDataReady(address(_shortsTracker), _value); } function disableIsGlobalShortDataReady(IShortsTracker _shortsTracker) external onlyAdmin { _shortsTracker.setIsGlobalShortDataReady(false); emit SetIsGlobalShortDataReady(address(_shortsTracker), false); } function setGlobalShortAveragePrices(IShortsTracker _shortsTracker, address[] calldata _tokens, uint256[] calldata _averagePrices) external onlyHandler { _shortsTracker.setIsGlobalShortDataReady(false); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; uint256 oldAveragePrice = _shortsTracker.globalShortAveragePrices(token); uint256 newAveragePrice = _averagePrices[i]; uint256 diff = newAveragePrice > oldAveragePrice ? newAveragePrice.sub(oldAveragePrice) : oldAveragePrice.sub(newAveragePrice); require(diff.mul(BASIS_POINTS_DIVISOR).div(oldAveragePrice) < maxAveragePriceChange, "ShortsTrackerTimelock: too big change"); require(block.timestamp >= lastUpdated[token].add(averagePriceUpdateDelay), "ShortsTrackerTimelock: too early"); lastUpdated[token] = block.timestamp; emit GlobalShortAveragePriceUpdated(token, oldAveragePrice, newAveragePrice); } _shortsTracker.setInitData(_tokens, _averagePrices); } function cancelAction(bytes32 _action) external onlyAdmin { _clearAction(_action); } function _setPendingAction(bytes32 _action) private { require(pendingActions[_action] == 0, "ShortsTrackerTimelock: action already signalled"); pendingActions[_action] = block.timestamp.add(buffer); emit SignalPendingAction(_action); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action] != 0, "ShortsTrackerTimelock: action not signalled"); require(pendingActions[_action] <= block.timestamp, "ShortsTrackerTimelock: action time not yet passed"); } function _clearAction(bytes32 _action) private { require(pendingActions[_action] != 0, "ShortsTrackerTimelock: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../access/Governable.sol"; import "./interfaces/IShortsTracker.sol"; import "./interfaces/IVault.sol"; contract ShortsTracker is Governable, IShortsTracker { using SafeMath for uint256; event GlobalShortDataUpdated(address indexed token, uint256 globalShortSize, uint256 globalShortAveragePrice); uint256 public constant MAX_INT256 = uint256(type(int256).max); IVault public vault; mapping (address => bool) public isHandler; mapping (bytes32 => bytes32) public data; mapping (address => uint256) override public globalShortAveragePrices; bool override public isGlobalShortDataReady; modifier onlyHandler() { require(isHandler[msg.sender], "ShortsTracker: forbidden"); _; } constructor(address _vault) public { vault = IVault(_vault); } function setHandler(address _handler, bool _isActive) external onlyGov { require(_handler != address(0), "ShortsTracker: invalid _handler"); isHandler[_handler] = _isActive; } function _setGlobalShortAveragePrice(address _token, uint256 _averagePrice) internal { globalShortAveragePrices[_token] = _averagePrice; } function setIsGlobalShortDataReady(bool value) override external onlyGov { isGlobalShortDataReady = value; } function updateGlobalShortData( address _account, address _collateralToken, address _indexToken, bool _isLong, uint256 _sizeDelta, uint256 _markPrice, bool _isIncrease ) override external onlyHandler { if (_isLong || _sizeDelta == 0) { return; } if (!isGlobalShortDataReady) { return; } (uint256 globalShortSize, uint256 globalShortAveragePrice) = getNextGlobalShortData( _account, _collateralToken, _indexToken, _markPrice, _sizeDelta, _isIncrease ); _setGlobalShortAveragePrice(_indexToken, globalShortAveragePrice); emit GlobalShortDataUpdated(_indexToken, globalShortSize, globalShortAveragePrice); } function getGlobalShortDelta(address _token) public view returns (bool, uint256) { uint256 size = vault.globalShortSizes(_token); uint256 averagePrice = globalShortAveragePrices[_token]; if (size == 0) { return (false, 0); } uint256 nextPrice = IVault(vault).getMaxPrice(_token); uint256 priceDelta = averagePrice > nextPrice ? averagePrice.sub(nextPrice) : nextPrice.sub(averagePrice); uint256 delta = size.mul(priceDelta).div(averagePrice); bool hasProfit = averagePrice > nextPrice; return (hasProfit, delta); } function setInitData(address[] calldata _tokens, uint256[] calldata _averagePrices) override external onlyGov { require(!isGlobalShortDataReady, "ShortsTracker: already migrated"); for (uint256 i = 0; i < _tokens.length; i++) { globalShortAveragePrices[_tokens[i]] = _averagePrices[i]; } isGlobalShortDataReady = true; } function getNextGlobalShortData( address _account, address _collateralToken, address _indexToken, uint256 _nextPrice, uint256 _sizeDelta, bool _isIncrease ) override public view returns (uint256, uint256) { int256 realisedPnl = getRealisedPnl(_account,_collateralToken, _indexToken, _sizeDelta, _isIncrease); uint256 averagePrice = globalShortAveragePrices[_indexToken]; uint256 priceDelta = averagePrice > _nextPrice ? averagePrice.sub(_nextPrice) : _nextPrice.sub(averagePrice); uint256 nextSize; uint256 delta; // avoid stack to deep { uint256 size = vault.globalShortSizes(_indexToken); nextSize = _isIncrease ? size.add(_sizeDelta) : size.sub(_sizeDelta); if (nextSize == 0) { return (0, 0); } if (averagePrice == 0) { return (nextSize, _nextPrice); } delta = size.mul(priceDelta).div(averagePrice); } uint256 nextAveragePrice = _getNextGlobalAveragePrice( averagePrice, _nextPrice, nextSize, delta, realisedPnl ); return (nextSize, nextAveragePrice); } function getRealisedPnl( address _account, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isIncrease ) public view returns (int256) { if (_isIncrease) { return 0; } IVault _vault = vault; (uint256 size, /*uint256 collateral*/, uint256 averagePrice, , , , , uint256 lastIncreasedTime) = _vault.getPosition(_account, _collateralToken, _indexToken, false); (bool hasProfit, uint256 delta) = _vault.getDelta(_indexToken, size, averagePrice, false, lastIncreasedTime); // get the proportional change in pnl uint256 adjustedDelta = _sizeDelta.mul(delta).div(size); require(adjustedDelta < MAX_INT256, "ShortsTracker: overflow"); return hasProfit ? int256(adjustedDelta) : -int256(adjustedDelta); } function _getNextGlobalAveragePrice( uint256 _averagePrice, uint256 _nextPrice, uint256 _nextSize, uint256 _delta, int256 _realisedPnl ) public pure returns (uint256) { (bool hasProfit, uint256 nextDelta) = _getNextDelta(_delta, _averagePrice, _nextPrice, _realisedPnl); uint256 nextAveragePrice = _nextPrice .mul(_nextSize) .div(hasProfit ? _nextSize.sub(nextDelta) : _nextSize.add(nextDelta)); return nextAveragePrice; } function _getNextDelta( uint256 _delta, uint256 _averagePrice, uint256 _nextPrice, int256 _realisedPnl ) internal pure returns (bool, uint256) { // global delta 10000, realised pnl 1000 => new pnl 9000 // global delta 10000, realised pnl -1000 => new pnl 11000 // global delta -10000, realised pnl 1000 => new pnl -11000 // global delta -10000, realised pnl -1000 => new pnl -9000 // global delta 10000, realised pnl 11000 => new pnl -1000 (flips sign) // global delta -10000, realised pnl -11000 => new pnl 1000 (flips sign) bool hasProfit = _averagePrice > _nextPrice; if (hasProfit) { // global shorts pnl is positive if (_realisedPnl > 0) { if (uint256(_realisedPnl) > _delta) { _delta = uint256(_realisedPnl).sub(_delta); hasProfit = false; } else { _delta = _delta.sub(uint256(_realisedPnl)); } } else { _delta = _delta.add(uint256(-_realisedPnl)); } return (hasProfit, _delta); } if (_realisedPnl > 0) { _delta = _delta.add(uint256(_realisedPnl)); } else { if (uint256(-_realisedPnl) > _delta) { _delta = uint256(-_realisedPnl).sub(_delta); hasProfit = true; } else { _delta = _delta.sub(uint256(-_realisedPnl)); } } return (hasProfit, _delta); } }
// SPDX-License-Identifier: MIT import "../ShortsTracker.sol"; pragma solidity 0.6.12; contract ShortsTrackerTest is ShortsTracker { constructor(address _vault) public ShortsTracker(_vault) {} function getNextGlobalShortDataWithRealisedPnl( address _indexToken, uint256 _nextPrice, uint256 _sizeDelta, int256 _realisedPnl, bool _isIncrease ) public view returns (uint256, uint256) { uint256 averagePrice = globalShortAveragePrices[_indexToken]; uint256 priceDelta = averagePrice > _nextPrice ? averagePrice.sub(_nextPrice) : _nextPrice.sub(averagePrice); uint256 nextSize; uint256 delta; // avoid stack to deep { uint256 size = vault.globalShortSizes(_indexToken); nextSize = _isIncrease ? size.add(_sizeDelta) : size.sub(_sizeDelta); if (nextSize == 0) { return (0, 0); } if (averagePrice == 0) { return (nextSize, _nextPrice); } delta = size.mul(priceDelta).div(averagePrice); } uint256 nextAveragePrice = _getNextGlobalAveragePrice( averagePrice, _nextPrice, nextSize, delta, _realisedPnl ); return (nextSize, nextAveragePrice); } function setGlobalShortAveragePrice(address _token, uint256 _averagePrice) public { globalShortAveragePrices[_token] = _averagePrice; } }
// SPDX-License-Identifier: MIT import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IGlpManager.sol"; import "./interfaces/IShortsTracker.sol"; import "../tokens/interfaces/IUSDG.sol"; import "../tokens/interfaces/IMintable.sol"; import "../access/Governable.sol"; pragma solidity 0.6.12; contract GlpManager is ReentrancyGuard, Governable, IGlpManager { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant USDG_DECIMALS = 18; uint256 public constant GLP_PRECISION = 10 ** 18; uint256 public constant MAX_COOLDOWN_DURATION = 48 hours; uint256 public constant BASIS_POINTS_DIVISOR = 10000; IVault public override vault; IShortsTracker public shortsTracker; address public override usdg; address public override glp; uint256 public override cooldownDuration; mapping (address => uint256) public override lastAddedAt; uint256 public aumAddition; uint256 public aumDeduction; bool public inPrivateMode; uint256 public shortsTrackerAveragePriceWeight; mapping (address => bool) public isHandler; event AddLiquidity( address account, address token, uint256 amount, uint256 aumInUsdg, uint256 glpSupply, uint256 usdgAmount, uint256 mintAmount ); event RemoveLiquidity( address account, address token, uint256 glpAmount, uint256 aumInUsdg, uint256 glpSupply, uint256 usdgAmount, uint256 amountOut ); constructor(address _vault, address _usdg, address _glp, address _shortsTracker, uint256 _cooldownDuration) public { gov = msg.sender; vault = IVault(_vault); usdg = _usdg; glp = _glp; shortsTracker = IShortsTracker(_shortsTracker); cooldownDuration = _cooldownDuration; } function setInPrivateMode(bool _inPrivateMode) external onlyGov { inPrivateMode = _inPrivateMode; } function setShortsTracker(IShortsTracker _shortsTracker) external onlyGov { shortsTracker = _shortsTracker; } function setShortsTrackerAveragePriceWeight(uint256 _shortsTrackerAveragePriceWeight) external override onlyGov { require(shortsTrackerAveragePriceWeight <= BASIS_POINTS_DIVISOR, "GlpManager: invalid weight"); shortsTrackerAveragePriceWeight = _shortsTrackerAveragePriceWeight; } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; } function setCooldownDuration(uint256 _cooldownDuration) external override onlyGov { require(_cooldownDuration <= MAX_COOLDOWN_DURATION, "GlpManager: invalid _cooldownDuration"); cooldownDuration = _cooldownDuration; } function setAumAdjustment(uint256 _aumAddition, uint256 _aumDeduction) external onlyGov { aumAddition = _aumAddition; aumDeduction = _aumDeduction; } function addLiquidity(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external override nonReentrant returns (uint256) { if (inPrivateMode) { revert("GlpManager: action not enabled"); } return _addLiquidity(msg.sender, msg.sender, _token, _amount, _minUsdg, _minGlp); } function addLiquidityForAccount(address _fundingAccount, address _account, address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external override nonReentrant returns (uint256) { _validateHandler(); return _addLiquidity(_fundingAccount, _account, _token, _amount, _minUsdg, _minGlp); } function removeLiquidity(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external override nonReentrant returns (uint256) { if (inPrivateMode) { revert("GlpManager: action not enabled"); } return _removeLiquidity(msg.sender, _tokenOut, _glpAmount, _minOut, _receiver); } function removeLiquidityForAccount(address _account, address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external override nonReentrant returns (uint256) { _validateHandler(); return _removeLiquidity(_account, _tokenOut, _glpAmount, _minOut, _receiver); } function getPrice(bool _maximise) external view returns (uint256) { uint256 aum = getAum(_maximise); uint256 supply = IERC20(glp).totalSupply(); return aum.mul(GLP_PRECISION).div(supply); } function getAums() public view returns (uint256[] memory) { uint256[] memory amounts = new uint256[](2); amounts[0] = getAum(true); amounts[1] = getAum(false); return amounts; } function getAumInUsdg(bool maximise) public override view returns (uint256) { uint256 aum = getAum(maximise); return aum.mul(10 ** USDG_DECIMALS).div(PRICE_PRECISION); } function getAum(bool maximise) public view returns (uint256) { uint256 length = vault.allWhitelistedTokensLength(); uint256 aum = aumAddition; uint256 shortProfits = 0; IVault _vault = vault; for (uint256 i = 0; i < length; i++) { address token = vault.allWhitelistedTokens(i); bool isWhitelisted = vault.whitelistedTokens(token); if (!isWhitelisted) { continue; } uint256 price = maximise ? _vault.getMaxPrice(token) : _vault.getMinPrice(token); uint256 poolAmount = _vault.poolAmounts(token); uint256 decimals = _vault.tokenDecimals(token); if (_vault.stableTokens(token)) { aum = aum.add(poolAmount.mul(price).div(10 ** decimals)); } else { // add global short profit / loss uint256 size = _vault.globalShortSizes(token); if (size > 0) { (uint256 delta, bool hasProfit) = getGlobalShortDelta(token, price, size); if (!hasProfit) { // add losses from shorts aum = aum.add(delta); } else { shortProfits = shortProfits.add(delta); } } aum = aum.add(_vault.guaranteedUsd(token)); uint256 reservedAmount = _vault.reservedAmounts(token); aum = aum.add(poolAmount.sub(reservedAmount).mul(price).div(10 ** decimals)); } } aum = shortProfits > aum ? 0 : aum.sub(shortProfits); return aumDeduction > aum ? 0 : aum.sub(aumDeduction); } function getGlobalShortDelta(address _token, uint256 _price, uint256 _size) public view returns (uint256, bool) { uint256 averagePrice = getGlobalShortAveragePrice(_token); uint256 priceDelta = averagePrice > _price ? averagePrice.sub(_price) : _price.sub(averagePrice); uint256 delta = _size.mul(priceDelta).div(averagePrice); return (delta, averagePrice > _price); } function getGlobalShortAveragePrice(address _token) public view returns (uint256) { IShortsTracker _shortsTracker = shortsTracker; if (address(_shortsTracker) == address(0) || !_shortsTracker.isGlobalShortDataReady()) { return vault.globalShortAveragePrices(_token); } uint256 _shortsTrackerAveragePriceWeight = shortsTrackerAveragePriceWeight; if (_shortsTrackerAveragePriceWeight == 0) { return vault.globalShortAveragePrices(_token); } else if (_shortsTrackerAveragePriceWeight == BASIS_POINTS_DIVISOR) { return _shortsTracker.globalShortAveragePrices(_token); } uint256 vaultAveragePrice = vault.globalShortAveragePrices(_token); uint256 shortsTrackerAveragePrice = _shortsTracker.globalShortAveragePrices(_token); return vaultAveragePrice.mul(BASIS_POINTS_DIVISOR.sub(_shortsTrackerAveragePriceWeight)) .add(shortsTrackerAveragePrice.mul(_shortsTrackerAveragePriceWeight)) .div(BASIS_POINTS_DIVISOR); } function _addLiquidity(address _fundingAccount, address _account, address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) private returns (uint256) { require(_amount > 0, "GlpManager: invalid _amount"); // calculate aum before buyUSDG uint256 aumInUsdg = getAumInUsdg(true); uint256 glpSupply = IERC20(glp).totalSupply(); IERC20(_token).safeTransferFrom(_fundingAccount, address(vault), _amount); uint256 usdgAmount = vault.buyUSDG(_token, address(this)); require(usdgAmount >= _minUsdg, "GlpManager: insufficient USDG output"); uint256 mintAmount = aumInUsdg == 0 ? usdgAmount : usdgAmount.mul(glpSupply).div(aumInUsdg); require(mintAmount >= _minGlp, "GlpManager: insufficient GLP output"); IMintable(glp).mint(_account, mintAmount); lastAddedAt[_account] = block.timestamp; emit AddLiquidity(_account, _token, _amount, aumInUsdg, glpSupply, usdgAmount, mintAmount); return mintAmount; } function _removeLiquidity(address _account, address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) private returns (uint256) { require(_glpAmount > 0, "GlpManager: invalid _glpAmount"); require(lastAddedAt[_account].add(cooldownDuration) <= block.timestamp, "GlpManager: cooldown duration not yet passed"); // calculate aum before sellUSDG uint256 aumInUsdg = getAumInUsdg(false); uint256 glpSupply = IERC20(glp).totalSupply(); uint256 usdgAmount = _glpAmount.mul(aumInUsdg).div(glpSupply); uint256 usdgBalance = IERC20(usdg).balanceOf(address(this)); if (usdgAmount > usdgBalance) { IUSDG(usdg).mint(address(this), usdgAmount.sub(usdgBalance)); } IMintable(glp).burn(_account, _glpAmount); IERC20(usdg).transfer(address(vault), usdgAmount); uint256 amountOut = vault.sellUSDG(_tokenOut, _receiver); require(amountOut >= _minOut, "GlpManager: insufficient output"); emit RemoveLiquidity(_account, _tokenOut, _glpAmount, aumInUsdg, glpSupply, usdgAmount, amountOut); return amountOut; } function _validateHandler() private view { require(isHandler[msg.sender], "GlpManager: forbidden"); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../core/interfaces/IGlpManager.sol"; import "./interfaces/IRewardTracker.sol"; import "./interfaces/IRewardTracker.sol"; import "../access/Governable.sol"; // provide a way to migrate staked GLP tokens by unstaking from the sender // and staking for the receiver // meant for a one-time use for a specified sender // requires the contract to be added as a handler for stakedGlpTracker and feeGlpTracker contract StakedGlpMigrator is Governable { using SafeMath for uint256; address public sender; address public glp; address public stakedGlpTracker; address public feeGlpTracker; bool public isEnabled = true; constructor( address _sender, address _glp, address _stakedGlpTracker, address _feeGlpTracker ) public { sender = _sender; glp = _glp; stakedGlpTracker = _stakedGlpTracker; feeGlpTracker = _feeGlpTracker; } function disable() external onlyGov { isEnabled = false; } function transfer(address _recipient, uint256 _amount) external onlyGov { _transfer(sender, _recipient, _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(isEnabled, "StakedGlpMigrator: not enabled"); require(_sender != address(0), "StakedGlpMigrator: transfer from the zero address"); require(_recipient != address(0), "StakedGlpMigrator: transfer to the zero address"); IRewardTracker(stakedGlpTracker).unstakeForAccount(_sender, feeGlpTracker, _amount, _sender); IRewardTracker(feeGlpTracker).unstakeForAccount(_sender, glp, _amount, _sender); IRewardTracker(feeGlpTracker).stakeForAccount(_sender, _recipient, glp, _amount); IRewardTracker(stakedGlpTracker).stakeForAccount(_recipient, _recipient, feeGlpTracker, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IRewardTracker.sol"; import "../access/Governable.sol"; contract StakeManager is Governable { function stakeForAccount( address _rewardTracker, address _account, address _token, uint256 _amount ) external onlyGov { IRewardTracker(_rewardTracker).stakeForAccount(_account, _account, _token, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../core/interfaces/IGlpManager.sol"; import "./interfaces/IRewardTracker.sol"; import "./interfaces/IRewardTracker.sol"; // provide a way to transfer staked GLP tokens by unstaking from the sender // and staking for the receiver // tests in RewardRouterV2.js contract StakedGlp { using SafeMath for uint256; string public constant name = "StakedGlp"; string public constant symbol = "sGLP"; uint8 public constant decimals = 18; address public glp; IGlpManager public glpManager; address public stakedGlpTracker; address public feeGlpTracker; mapping (address => mapping (address => uint256)) public allowances; event Approval(address indexed owner, address indexed spender, uint256 value); constructor( address _glp, IGlpManager _glpManager, address _stakedGlpTracker, address _feeGlpTracker ) public { glp = _glp; glpManager = _glpManager; stakedGlpTracker = _stakedGlpTracker; feeGlpTracker = _feeGlpTracker; } function allowance(address _owner, address _spender) external view returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transfer(address _recipient, uint256 _amount) external returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool) { uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "StakedGlp: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function balanceOf(address _account) external view returns (uint256) { return IRewardTracker(feeGlpTracker).depositBalances(_account, glp); } function totalSupply() external view returns (uint256) { return IERC20(stakedGlpTracker).totalSupply(); } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "StakedGlp: approve from the zero address"); require(_spender != address(0), "StakedGlp: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "StakedGlp: transfer from the zero address"); require(_recipient != address(0), "StakedGlp: transfer to the zero address"); require( glpManager.lastAddedAt(_sender).add(glpManager.cooldownDuration()) <= block.timestamp, "StakedGlp: cooldown duration not yet passed" ); IRewardTracker(stakedGlpTracker).unstakeForAccount(_sender, feeGlpTracker, _amount, _sender); IRewardTracker(feeGlpTracker).unstakeForAccount(_sender, glp, _amount, _sender); IRewardTracker(feeGlpTracker).stakeForAccount(_sender, _recipient, glp, _amount); IRewardTracker(stakedGlpTracker).stakeForAccount(_recipient, _recipient, feeGlpTracker, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRewardDistributor.sol"; import "./interfaces/IRewardTracker.sol"; import "../access/Governable.sol"; contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant PRECISION = 1e30; uint8 public constant decimals = 18; bool public isInitialized; string public name; string public symbol; address public distributor; mapping (address => bool) public isDepositToken; mapping (address => mapping (address => uint256)) public override depositBalances; mapping (address => uint256) public totalDepositSupply; uint256 public override totalSupply; mapping (address => uint256) public balances; mapping (address => mapping (address => uint256)) public allowances; uint256 public cumulativeRewardPerToken; mapping (address => uint256) public override stakedAmounts; mapping (address => uint256) public claimableReward; mapping (address => uint256) public previousCumulatedRewardPerToken; mapping (address => uint256) public override cumulativeRewards; mapping (address => uint256) public override averageStakedAmounts; bool public inPrivateTransferMode; bool public inPrivateStakingMode; bool public inPrivateClaimingMode; mapping (address => bool) public isHandler; event Claim(address receiver, uint256 amount); constructor(string memory _name, string memory _symbol) public { name = _name; symbol = _symbol; } function initialize( address[] memory _depositTokens, address _distributor ) external onlyGov { require(!isInitialized, "RewardTracker: already initialized"); isInitialized = true; for (uint256 i = 0; i < _depositTokens.length; i++) { address depositToken = _depositTokens[i]; isDepositToken[depositToken] = true; } distributor = _distributor; } function setDepositToken(address _depositToken, bool _isDepositToken) external onlyGov { isDepositToken[_depositToken] = _isDepositToken; } function setInPrivateTransferMode(bool _inPrivateTransferMode) external onlyGov { inPrivateTransferMode = _inPrivateTransferMode; } function setInPrivateStakingMode(bool _inPrivateStakingMode) external onlyGov { inPrivateStakingMode = _inPrivateStakingMode; } function setInPrivateClaimingMode(bool _inPrivateClaimingMode) external onlyGov { inPrivateClaimingMode = _inPrivateClaimingMode; } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function balanceOf(address _account) external view override returns (uint256) { return balances[_account]; } function stake(address _depositToken, uint256 _amount) external override nonReentrant { if (inPrivateStakingMode) { revert("RewardTracker: action not enabled"); } _stake(msg.sender, msg.sender, _depositToken, _amount); } function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount) external override nonReentrant { _validateHandler(); _stake(_fundingAccount, _account, _depositToken, _amount); } function unstake(address _depositToken, uint256 _amount) external override nonReentrant { if (inPrivateStakingMode) { revert("RewardTracker: action not enabled"); } _unstake(msg.sender, _depositToken, _amount, msg.sender); } function unstakeForAccount(address _account, address _depositToken, uint256 _amount, address _receiver) external override nonReentrant { _validateHandler(); _unstake(_account, _depositToken, _amount, _receiver); } function transfer(address _recipient, uint256 _amount) external override returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function allowance(address _owner, address _spender) external view override returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external override returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) { if (isHandler[msg.sender]) { _transfer(_sender, _recipient, _amount); return true; } uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "RewardTracker: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function tokensPerInterval() external override view returns (uint256) { return IRewardDistributor(distributor).tokensPerInterval(); } function updateRewards() external override nonReentrant { _updateRewards(address(0)); } function claim(address _receiver) external override nonReentrant returns (uint256) { if (inPrivateClaimingMode) { revert("RewardTracker: action not enabled"); } return _claim(msg.sender, _receiver); } function claimForAccount(address _account, address _receiver) external override nonReentrant returns (uint256) { _validateHandler(); return _claim(_account, _receiver); } function claimable(address _account) public override view returns (uint256) { uint256 stakedAmount = stakedAmounts[_account]; if (stakedAmount == 0) { return claimableReward[_account]; } uint256 supply = totalSupply; uint256 pendingRewards = IRewardDistributor(distributor).pendingRewards().mul(PRECISION); uint256 nextCumulativeRewardPerToken = cumulativeRewardPerToken.add(pendingRewards.div(supply)); return claimableReward[_account].add( stakedAmount.mul(nextCumulativeRewardPerToken.sub(previousCumulatedRewardPerToken[_account])).div(PRECISION)); } function rewardToken() public view returns (address) { return IRewardDistributor(distributor).rewardToken(); } function _claim(address _account, address _receiver) private returns (uint256) { _updateRewards(_account); uint256 tokenAmount = claimableReward[_account]; claimableReward[_account] = 0; if (tokenAmount > 0) { IERC20(rewardToken()).safeTransfer(_receiver, tokenAmount); emit Claim(_account, tokenAmount); } return tokenAmount; } function _mint(address _account, uint256 _amount) internal { require(_account != address(0), "RewardTracker: mint to the zero address"); totalSupply = totalSupply.add(_amount); balances[_account] = balances[_account].add(_amount); emit Transfer(address(0), _account, _amount); } function _burn(address _account, uint256 _amount) internal { require(_account != address(0), "RewardTracker: burn from the zero address"); balances[_account] = balances[_account].sub(_amount, "RewardTracker: burn amount exceeds balance"); totalSupply = totalSupply.sub(_amount); emit Transfer(_account, address(0), _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "RewardTracker: transfer from the zero address"); require(_recipient != address(0), "RewardTracker: transfer to the zero address"); if (inPrivateTransferMode) { _validateHandler(); } balances[_sender] = balances[_sender].sub(_amount, "RewardTracker: transfer amount exceeds balance"); balances[_recipient] = balances[_recipient].add(_amount); emit Transfer(_sender, _recipient,_amount); } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "RewardTracker: approve from the zero address"); require(_spender != address(0), "RewardTracker: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } function _validateHandler() private view { require(isHandler[msg.sender], "RewardTracker: forbidden"); } function _stake(address _fundingAccount, address _account, address _depositToken, uint256 _amount) private { require(_amount > 0, "RewardTracker: invalid _amount"); require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken"); IERC20(_depositToken).safeTransferFrom(_fundingAccount, address(this), _amount); _updateRewards(_account); stakedAmounts[_account] = stakedAmounts[_account].add(_amount); depositBalances[_account][_depositToken] = depositBalances[_account][_depositToken].add(_amount); totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken].add(_amount); _mint(_account, _amount); } function _unstake(address _account, address _depositToken, uint256 _amount, address _receiver) private { require(_amount > 0, "RewardTracker: invalid _amount"); require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken"); _updateRewards(_account); uint256 stakedAmount = stakedAmounts[_account]; require(stakedAmounts[_account] >= _amount, "RewardTracker: _amount exceeds stakedAmount"); stakedAmounts[_account] = stakedAmount.sub(_amount); uint256 depositBalance = depositBalances[_account][_depositToken]; require(depositBalance >= _amount, "RewardTracker: _amount exceeds depositBalance"); depositBalances[_account][_depositToken] = depositBalance.sub(_amount); totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken].sub(_amount); _burn(_account, _amount); IERC20(_depositToken).safeTransfer(_receiver, _amount); } function _updateRewards(address _account) private { uint256 blockReward = IRewardDistributor(distributor).distribute(); uint256 supply = totalSupply; uint256 _cumulativeRewardPerToken = cumulativeRewardPerToken; if (supply > 0 && blockReward > 0) { _cumulativeRewardPerToken = _cumulativeRewardPerToken.add(blockReward.mul(PRECISION).div(supply)); cumulativeRewardPerToken = _cumulativeRewardPerToken; } // cumulativeRewardPerToken can only increase // so if cumulativeRewardPerToken is zero, it means there are no rewards yet if (_cumulativeRewardPerToken == 0) { return; } if (_account != address(0)) { uint256 stakedAmount = stakedAmounts[_account]; uint256 accountReward = stakedAmount.mul(_cumulativeRewardPerToken.sub(previousCumulatedRewardPerToken[_account])).div(PRECISION); uint256 _claimableReward = claimableReward[_account].add(accountReward); claimableReward[_account] = _claimableReward; previousCumulatedRewardPerToken[_account] = _cumulativeRewardPerToken; if (_claimableReward > 0 && stakedAmounts[_account] > 0) { uint256 nextCumulativeReward = cumulativeRewards[_account].add(accountReward); averageStakedAmounts[_account] = averageStakedAmounts[_account].mul(cumulativeRewards[_account]).div(nextCumulativeReward) .add(stakedAmount.mul(accountReward).div(nextCumulativeReward)); cumulativeRewards[_account] = nextCumulativeReward; } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IRewardDistributor { function rewardToken() external view returns (address); function tokensPerInterval() external view returns (uint256); function pendingRewards() external view returns (uint256); function distribute() external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRewardDistributor.sol"; import "./interfaces/IRewardTracker.sol"; import "../access/Governable.sol"; contract RewardDistributor is IRewardDistributor, ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; address public override rewardToken; uint256 public override tokensPerInterval; uint256 public lastDistributionTime; address public rewardTracker; address public admin; event Distribute(uint256 amount); event TokensPerIntervalChange(uint256 amount); modifier onlyAdmin() { require(msg.sender == admin, "RewardDistributor: forbidden"); _; } constructor(address _rewardToken, address _rewardTracker) public { rewardToken = _rewardToken; rewardTracker = _rewardTracker; admin = msg.sender; } function setAdmin(address _admin) external onlyGov { admin = _admin; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function updateLastDistributionTime() external onlyAdmin { lastDistributionTime = block.timestamp; } function setTokensPerInterval(uint256 _amount) external onlyAdmin { require(lastDistributionTime != 0, "RewardDistributor: invalid lastDistributionTime"); IRewardTracker(rewardTracker).updateRewards(); tokensPerInterval = _amount; emit TokensPerIntervalChange(_amount); } function pendingRewards() public view override returns (uint256) { if (block.timestamp == lastDistributionTime) { return 0; } uint256 timeDiff = block.timestamp.sub(lastDistributionTime); return tokensPerInterval.mul(timeDiff); } function distribute() external override returns (uint256) { require(msg.sender == rewardTracker, "RewardDistributor: invalid msg.sender"); uint256 amount = pendingRewards(); if (amount == 0) { return 0; } lastDistributionTime = block.timestamp; uint256 balance = IERC20(rewardToken).balanceOf(address(this)); if (amount > balance) { amount = balance; } IERC20(rewardToken).safeTransfer(msg.sender, amount); emit Distribute(amount); return amount; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRewardDistributor.sol"; import "./interfaces/IRewardTracker.sol"; import "../access/Governable.sol"; contract BonusDistributor is IRewardDistributor, ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant BONUS_DURATION = 365 days; uint256 public bonusMultiplierBasisPoints; address public override rewardToken; uint256 public lastDistributionTime; address public rewardTracker; address public admin; event Distribute(uint256 amount); event BonusMultiplierChange(uint256 amount); modifier onlyAdmin() { require(msg.sender == admin, "BonusDistributor: forbidden"); _; } constructor(address _rewardToken, address _rewardTracker) public { rewardToken = _rewardToken; rewardTracker = _rewardTracker; admin = msg.sender; } function setAdmin(address _admin) external onlyGov { admin = _admin; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function updateLastDistributionTime() external onlyAdmin { lastDistributionTime = block.timestamp; } function setBonusMultiplier(uint256 _bonusMultiplierBasisPoints) external onlyAdmin { require(lastDistributionTime != 0, "BonusDistributor: invalid lastDistributionTime"); IRewardTracker(rewardTracker).updateRewards(); bonusMultiplierBasisPoints = _bonusMultiplierBasisPoints; emit BonusMultiplierChange(_bonusMultiplierBasisPoints); } function tokensPerInterval() public view override returns (uint256) { uint256 supply = IERC20(rewardTracker).totalSupply(); return supply.mul(bonusMultiplierBasisPoints).div(BASIS_POINTS_DIVISOR).div(BONUS_DURATION); } function pendingRewards() public view override returns (uint256) { if (block.timestamp == lastDistributionTime) { return 0; } uint256 supply = IERC20(rewardTracker).totalSupply(); uint256 timeDiff = block.timestamp.sub(lastDistributionTime); return timeDiff.mul(supply).mul(bonusMultiplierBasisPoints).div(BASIS_POINTS_DIVISOR).div(BONUS_DURATION); } function distribute() external override returns (uint256) { require(msg.sender == rewardTracker, "BonusDistributor: invalid msg.sender"); uint256 amount = pendingRewards(); if (amount == 0) { return 0; } lastDistributionTime = block.timestamp; uint256 balance = IERC20(rewardToken).balanceOf(address(this)); if (amount > balance) { amount = balance; } IERC20(rewardToken).safeTransfer(msg.sender, amount); emit Distribute(amount); return amount; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../libraries/utils/Address.sol"; import "./interfaces/IRewardTracker.sol"; import "./interfaces/IRewardRouterV2.sol"; import "./interfaces/IVester.sol"; import "../tokens/interfaces/IMintable.sol"; import "../tokens/interfaces/IWETH.sol"; import "../core/interfaces/IGlpManager.sol"; import "../access/Governable.sol"; contract RewardRouterV2 is IRewardRouterV2, ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address payable; bool public isInitialized; address public weth; address public gmx; address public esGmx; address public bnGmx; address public glp; // GMX Liquidity Provider token address public stakedGmxTracker; address public bonusGmxTracker; address public feeGmxTracker; address public override stakedGlpTracker; address public override feeGlpTracker; address public glpManager; address public gmxVester; address public glpVester; mapping (address => address) public pendingReceivers; event StakeGmx(address account, address token, uint256 amount); event UnstakeGmx(address account, address token, uint256 amount); event StakeGlp(address account, uint256 amount); event UnstakeGlp(address account, uint256 amount); receive() external payable { require(msg.sender == weth, "Router: invalid sender"); } function initialize( address _weth, address _gmx, address _esGmx, address _bnGmx, address _glp, address _stakedGmxTracker, address _bonusGmxTracker, address _feeGmxTracker, address _feeGlpTracker, address _stakedGlpTracker, address _glpManager, address _gmxVester, address _glpVester ) external onlyGov { require(!isInitialized, "RewardRouter: already initialized"); isInitialized = true; weth = _weth; gmx = _gmx; esGmx = _esGmx; bnGmx = _bnGmx; glp = _glp; stakedGmxTracker = _stakedGmxTracker; bonusGmxTracker = _bonusGmxTracker; feeGmxTracker = _feeGmxTracker; feeGlpTracker = _feeGlpTracker; stakedGlpTracker = _stakedGlpTracker; glpManager = _glpManager; gmxVester = _gmxVester; glpVester = _glpVester; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function batchStakeGmxForAccount(address[] memory _accounts, uint256[] memory _amounts) external nonReentrant onlyGov { address _gmx = gmx; for (uint256 i = 0; i < _accounts.length; i++) { _stakeGmx(msg.sender, _accounts[i], _gmx, _amounts[i]); } } function stakeGmxForAccount(address _account, uint256 _amount) external nonReentrant onlyGov { _stakeGmx(msg.sender, _account, gmx, _amount); } function stakeGmx(uint256 _amount) external nonReentrant { _stakeGmx(msg.sender, msg.sender, gmx, _amount); } function stakeEsGmx(uint256 _amount) external nonReentrant { _stakeGmx(msg.sender, msg.sender, esGmx, _amount); } function unstakeGmx(uint256 _amount) external nonReentrant { _unstakeGmx(msg.sender, gmx, _amount, true); } function unstakeEsGmx(uint256 _amount) external nonReentrant { _unstakeGmx(msg.sender, esGmx, _amount, true); } function mintAndStakeGlp(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external nonReentrant returns (uint256) { require(_amount > 0, "RewardRouter: invalid _amount"); address account = msg.sender; uint256 glpAmount = IGlpManager(glpManager).addLiquidityForAccount(account, account, _token, _amount, _minUsdg, _minGlp); IRewardTracker(feeGlpTracker).stakeForAccount(account, account, glp, glpAmount); IRewardTracker(stakedGlpTracker).stakeForAccount(account, account, feeGlpTracker, glpAmount); emit StakeGlp(account, glpAmount); return glpAmount; } function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable nonReentrant returns (uint256) { require(msg.value > 0, "RewardRouter: invalid msg.value"); IWETH(weth).deposit{value: msg.value}(); IERC20(weth).approve(glpManager, msg.value); address account = msg.sender; uint256 glpAmount = IGlpManager(glpManager).addLiquidityForAccount(address(this), account, weth, msg.value, _minUsdg, _minGlp); IRewardTracker(feeGlpTracker).stakeForAccount(account, account, glp, glpAmount); IRewardTracker(stakedGlpTracker).stakeForAccount(account, account, feeGlpTracker, glpAmount); emit StakeGlp(account, glpAmount); return glpAmount; } function unstakeAndRedeemGlp(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external nonReentrant returns (uint256) { require(_glpAmount > 0, "RewardRouter: invalid _glpAmount"); address account = msg.sender; IRewardTracker(stakedGlpTracker).unstakeForAccount(account, feeGlpTracker, _glpAmount, account); IRewardTracker(feeGlpTracker).unstakeForAccount(account, glp, _glpAmount, account); uint256 amountOut = IGlpManager(glpManager).removeLiquidityForAccount(account, _tokenOut, _glpAmount, _minOut, _receiver); emit UnstakeGlp(account, _glpAmount); return amountOut; } function unstakeAndRedeemGlpETH(uint256 _glpAmount, uint256 _minOut, address payable _receiver) external nonReentrant returns (uint256) { require(_glpAmount > 0, "RewardRouter: invalid _glpAmount"); address account = msg.sender; IRewardTracker(stakedGlpTracker).unstakeForAccount(account, feeGlpTracker, _glpAmount, account); IRewardTracker(feeGlpTracker).unstakeForAccount(account, glp, _glpAmount, account); uint256 amountOut = IGlpManager(glpManager).removeLiquidityForAccount(account, weth, _glpAmount, _minOut, address(this)); IWETH(weth).withdraw(amountOut); _receiver.sendValue(amountOut); emit UnstakeGlp(account, _glpAmount); return amountOut; } function claim() external nonReentrant { address account = msg.sender; IRewardTracker(feeGmxTracker).claimForAccount(account, account); IRewardTracker(feeGlpTracker).claimForAccount(account, account); IRewardTracker(stakedGmxTracker).claimForAccount(account, account); IRewardTracker(stakedGlpTracker).claimForAccount(account, account); } function claimEsGmx() external nonReentrant { address account = msg.sender; IRewardTracker(stakedGmxTracker).claimForAccount(account, account); IRewardTracker(stakedGlpTracker).claimForAccount(account, account); } function claimFees() external nonReentrant { address account = msg.sender; IRewardTracker(feeGmxTracker).claimForAccount(account, account); IRewardTracker(feeGlpTracker).claimForAccount(account, account); } function compound() external nonReentrant { _compound(msg.sender); } function compoundForAccount(address _account) external nonReentrant onlyGov { _compound(_account); } function handleRewards( bool _shouldClaimGmx, bool _shouldStakeGmx, bool _shouldClaimEsGmx, bool _shouldStakeEsGmx, bool _shouldStakeMultiplierPoints, bool _shouldClaimWeth, bool _shouldConvertWethToEth ) external nonReentrant { address account = msg.sender; uint256 gmxAmount = 0; if (_shouldClaimGmx) { uint256 gmxAmount0 = IVester(gmxVester).claimForAccount(account, account); uint256 gmxAmount1 = IVester(glpVester).claimForAccount(account, account); gmxAmount = gmxAmount0.add(gmxAmount1); } if (_shouldStakeGmx && gmxAmount > 0) { _stakeGmx(account, account, gmx, gmxAmount); } uint256 esGmxAmount = 0; if (_shouldClaimEsGmx) { uint256 esGmxAmount0 = IRewardTracker(stakedGmxTracker).claimForAccount(account, account); uint256 esGmxAmount1 = IRewardTracker(stakedGlpTracker).claimForAccount(account, account); esGmxAmount = esGmxAmount0.add(esGmxAmount1); } if (_shouldStakeEsGmx && esGmxAmount > 0) { _stakeGmx(account, account, esGmx, esGmxAmount); } if (_shouldStakeMultiplierPoints) { uint256 bnGmxAmount = IRewardTracker(bonusGmxTracker).claimForAccount(account, account); if (bnGmxAmount > 0) { IRewardTracker(feeGmxTracker).stakeForAccount(account, account, bnGmx, bnGmxAmount); } } if (_shouldClaimWeth) { if (_shouldConvertWethToEth) { uint256 weth0 = IRewardTracker(feeGmxTracker).claimForAccount(account, address(this)); uint256 weth1 = IRewardTracker(feeGlpTracker).claimForAccount(account, address(this)); uint256 wethAmount = weth0.add(weth1); IWETH(weth).withdraw(wethAmount); payable(account).sendValue(wethAmount); } else { IRewardTracker(feeGmxTracker).claimForAccount(account, account); IRewardTracker(feeGlpTracker).claimForAccount(account, account); } } } function batchCompoundForAccounts(address[] memory _accounts) external nonReentrant onlyGov { for (uint256 i = 0; i < _accounts.length; i++) { _compound(_accounts[i]); } } // the _validateReceiver function checks that the averageStakedAmounts and cumulativeRewards // values of an account are zero, this is to help ensure that vesting calculations can be // done correctly // averageStakedAmounts and cumulativeRewards are updated if the claimable reward for an account // is more than zero // it is possible for multiple transfers to be sent into a single account, using signalTransfer and // acceptTransfer, if those values have not been updated yet // for GLP transfers it is also possible to transfer GLP into an account using the StakedGlp contract function signalTransfer(address _receiver) external nonReentrant { require(IERC20(gmxVester).balanceOf(msg.sender) == 0, "RewardRouter: sender has vested tokens"); require(IERC20(glpVester).balanceOf(msg.sender) == 0, "RewardRouter: sender has vested tokens"); _validateReceiver(_receiver); pendingReceivers[msg.sender] = _receiver; } function acceptTransfer(address _sender) external nonReentrant { require(IERC20(gmxVester).balanceOf(_sender) == 0, "RewardRouter: sender has vested tokens"); require(IERC20(glpVester).balanceOf(_sender) == 0, "RewardRouter: sender has vested tokens"); address receiver = msg.sender; require(pendingReceivers[_sender] == receiver, "RewardRouter: transfer not signalled"); delete pendingReceivers[_sender]; _validateReceiver(receiver); _compound(_sender); uint256 stakedGmx = IRewardTracker(stakedGmxTracker).depositBalances(_sender, gmx); if (stakedGmx > 0) { _unstakeGmx(_sender, gmx, stakedGmx, false); _stakeGmx(_sender, receiver, gmx, stakedGmx); } uint256 stakedEsGmx = IRewardTracker(stakedGmxTracker).depositBalances(_sender, esGmx); if (stakedEsGmx > 0) { _unstakeGmx(_sender, esGmx, stakedEsGmx, false); _stakeGmx(_sender, receiver, esGmx, stakedEsGmx); } uint256 stakedBnGmx = IRewardTracker(feeGmxTracker).depositBalances(_sender, bnGmx); if (stakedBnGmx > 0) { IRewardTracker(feeGmxTracker).unstakeForAccount(_sender, bnGmx, stakedBnGmx, _sender); IRewardTracker(feeGmxTracker).stakeForAccount(_sender, receiver, bnGmx, stakedBnGmx); } uint256 esGmxBalance = IERC20(esGmx).balanceOf(_sender); if (esGmxBalance > 0) { IERC20(esGmx).transferFrom(_sender, receiver, esGmxBalance); } uint256 glpAmount = IRewardTracker(feeGlpTracker).depositBalances(_sender, glp); if (glpAmount > 0) { IRewardTracker(stakedGlpTracker).unstakeForAccount(_sender, feeGlpTracker, glpAmount, _sender); IRewardTracker(feeGlpTracker).unstakeForAccount(_sender, glp, glpAmount, _sender); IRewardTracker(feeGlpTracker).stakeForAccount(_sender, receiver, glp, glpAmount); IRewardTracker(stakedGlpTracker).stakeForAccount(receiver, receiver, feeGlpTracker, glpAmount); } IVester(gmxVester).transferStakeValues(_sender, receiver); IVester(glpVester).transferStakeValues(_sender, receiver); } function _validateReceiver(address _receiver) private view { require(IRewardTracker(stakedGmxTracker).averageStakedAmounts(_receiver) == 0, "RewardRouter: stakedGmxTracker.averageStakedAmounts > 0"); require(IRewardTracker(stakedGmxTracker).cumulativeRewards(_receiver) == 0, "RewardRouter: stakedGmxTracker.cumulativeRewards > 0"); require(IRewardTracker(bonusGmxTracker).averageStakedAmounts(_receiver) == 0, "RewardRouter: bonusGmxTracker.averageStakedAmounts > 0"); require(IRewardTracker(bonusGmxTracker).cumulativeRewards(_receiver) == 0, "RewardRouter: bonusGmxTracker.cumulativeRewards > 0"); require(IRewardTracker(feeGmxTracker).averageStakedAmounts(_receiver) == 0, "RewardRouter: feeGmxTracker.averageStakedAmounts > 0"); require(IRewardTracker(feeGmxTracker).cumulativeRewards(_receiver) == 0, "RewardRouter: feeGmxTracker.cumulativeRewards > 0"); require(IVester(gmxVester).transferredAverageStakedAmounts(_receiver) == 0, "RewardRouter: gmxVester.transferredAverageStakedAmounts > 0"); require(IVester(gmxVester).transferredCumulativeRewards(_receiver) == 0, "RewardRouter: gmxVester.transferredCumulativeRewards > 0"); require(IRewardTracker(stakedGlpTracker).averageStakedAmounts(_receiver) == 0, "RewardRouter: stakedGlpTracker.averageStakedAmounts > 0"); require(IRewardTracker(stakedGlpTracker).cumulativeRewards(_receiver) == 0, "RewardRouter: stakedGlpTracker.cumulativeRewards > 0"); require(IRewardTracker(feeGlpTracker).averageStakedAmounts(_receiver) == 0, "RewardRouter: feeGlpTracker.averageStakedAmounts > 0"); require(IRewardTracker(feeGlpTracker).cumulativeRewards(_receiver) == 0, "RewardRouter: feeGlpTracker.cumulativeRewards > 0"); require(IVester(glpVester).transferredAverageStakedAmounts(_receiver) == 0, "RewardRouter: gmxVester.transferredAverageStakedAmounts > 0"); require(IVester(glpVester).transferredCumulativeRewards(_receiver) == 0, "RewardRouter: gmxVester.transferredCumulativeRewards > 0"); require(IERC20(gmxVester).balanceOf(_receiver) == 0, "RewardRouter: gmxVester.balance > 0"); require(IERC20(glpVester).balanceOf(_receiver) == 0, "RewardRouter: glpVester.balance > 0"); } function _compound(address _account) private { _compoundGmx(_account); _compoundGlp(_account); } function _compoundGmx(address _account) private { uint256 esGmxAmount = IRewardTracker(stakedGmxTracker).claimForAccount(_account, _account); if (esGmxAmount > 0) { _stakeGmx(_account, _account, esGmx, esGmxAmount); } uint256 bnGmxAmount = IRewardTracker(bonusGmxTracker).claimForAccount(_account, _account); if (bnGmxAmount > 0) { IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bnGmx, bnGmxAmount); } } function _compoundGlp(address _account) private { uint256 esGmxAmount = IRewardTracker(stakedGlpTracker).claimForAccount(_account, _account); if (esGmxAmount > 0) { _stakeGmx(_account, _account, esGmx, esGmxAmount); } } function _stakeGmx(address _fundingAccount, address _account, address _token, uint256 _amount) private { require(_amount > 0, "RewardRouter: invalid _amount"); IRewardTracker(stakedGmxTracker).stakeForAccount(_fundingAccount, _account, _token, _amount); IRewardTracker(bonusGmxTracker).stakeForAccount(_account, _account, stakedGmxTracker, _amount); IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bonusGmxTracker, _amount); emit StakeGmx(_account, _token, _amount); } function _unstakeGmx(address _account, address _token, uint256 _amount, bool _shouldReduceBnGmx) private { require(_amount > 0, "RewardRouter: invalid _amount"); uint256 balance = IRewardTracker(stakedGmxTracker).stakedAmounts(_account); IRewardTracker(feeGmxTracker).unstakeForAccount(_account, bonusGmxTracker, _amount, _account); IRewardTracker(bonusGmxTracker).unstakeForAccount(_account, stakedGmxTracker, _amount, _account); IRewardTracker(stakedGmxTracker).unstakeForAccount(_account, _token, _amount, _account); if (_shouldReduceBnGmx) { uint256 bnGmxAmount = IRewardTracker(bonusGmxTracker).claimForAccount(_account, _account); if (bnGmxAmount > 0) { IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bnGmx, bnGmxAmount); } uint256 stakedBnGmx = IRewardTracker(feeGmxTracker).depositBalances(_account, bnGmx); if (stakedBnGmx > 0) { uint256 reductionAmount = stakedBnGmx.mul(_amount).div(balance); IRewardTracker(feeGmxTracker).unstakeForAccount(_account, bnGmx, reductionAmount, _account); IMintable(bnGmx).burn(_account, reductionAmount); } } emit UnstakeGmx(_account, _token, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../libraries/utils/Address.sol"; import "./interfaces/IRewardTracker.sol"; import "../tokens/interfaces/IMintable.sol"; import "../tokens/interfaces/IWETH.sol"; import "../core/interfaces/IGlpManager.sol"; import "../access/Governable.sol"; contract RewardRouter is ReentrancyGuard, Governable { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address payable; bool public isInitialized; address public weth; address public gmx; address public esGmx; address public bnGmx; address public glp; // GMX Liquidity Provider token address public stakedGmxTracker; address public bonusGmxTracker; address public feeGmxTracker; address public stakedGlpTracker; address public feeGlpTracker; address public glpManager; event StakeGmx(address account, uint256 amount); event UnstakeGmx(address account, uint256 amount); event StakeGlp(address account, uint256 amount); event UnstakeGlp(address account, uint256 amount); receive() external payable { require(msg.sender == weth, "Router: invalid sender"); } function initialize( address _weth, address _gmx, address _esGmx, address _bnGmx, address _glp, address _stakedGmxTracker, address _bonusGmxTracker, address _feeGmxTracker, address _feeGlpTracker, address _stakedGlpTracker, address _glpManager ) external onlyGov { require(!isInitialized, "RewardRouter: already initialized"); isInitialized = true; weth = _weth; gmx = _gmx; esGmx = _esGmx; bnGmx = _bnGmx; //Benen: bonus gmx?? glp = _glp; stakedGmxTracker = _stakedGmxTracker; bonusGmxTracker = _bonusGmxTracker; feeGmxTracker = _feeGmxTracker; feeGlpTracker = _feeGlpTracker; stakedGlpTracker = _stakedGlpTracker; glpManager = _glpManager; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov { IERC20(_token).safeTransfer(_account, _amount); } function batchStakeGmxForAccount(address[] memory _accounts, uint256[] memory _amounts) external nonReentrant onlyGov { address _gmx = gmx; for (uint256 i = 0; i < _accounts.length; i++) { _stakeGmx(msg.sender, _accounts[i], _gmx, _amounts[i]); } } function stakeGmxForAccount(address _account, uint256 _amount) external nonReentrant onlyGov { _stakeGmx(msg.sender, _account, gmx, _amount); } function stakeGmx(uint256 _amount) external nonReentrant { _stakeGmx(msg.sender, msg.sender, gmx, _amount); } function stakeEsGmx(uint256 _amount) external nonReentrant { _stakeGmx(msg.sender, msg.sender, esGmx, _amount); } function unstakeGmx(uint256 _amount) external nonReentrant { _unstakeGmx(msg.sender, gmx, _amount); } function unstakeEsGmx(uint256 _amount) external nonReentrant { _unstakeGmx(msg.sender, esGmx, _amount); } function mintAndStakeGlp(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external nonReentrant returns (uint256) { require(_amount > 0, "RewardRouter: invalid _amount"); address account = msg.sender; uint256 glpAmount = IGlpManager(glpManager).addLiquidityForAccount(account, account, _token, _amount, _minUsdg, _minGlp); IRewardTracker(feeGlpTracker).stakeForAccount(account, account, glp, glpAmount); IRewardTracker(stakedGlpTracker).stakeForAccount(account, account, feeGlpTracker, glpAmount); emit StakeGlp(account, glpAmount); return glpAmount; } function mintAndStakeGlpETH(uint256 _minUsdg, uint256 _minGlp) external payable nonReentrant returns (uint256) { require(msg.value > 0, "RewardRouter: invalid msg.value"); IWETH(weth).deposit{value: msg.value}(); IERC20(weth).approve(glpManager, msg.value); address account = msg.sender; uint256 glpAmount = IGlpManager(glpManager).addLiquidityForAccount(address(this), account, weth, msg.value, _minUsdg, _minGlp); IRewardTracker(feeGlpTracker).stakeForAccount(account, account, glp, glpAmount); IRewardTracker(stakedGlpTracker).stakeForAccount(account, account, feeGlpTracker, glpAmount); emit StakeGlp(account, glpAmount); return glpAmount; } function unstakeAndRedeemGlp(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external nonReentrant returns (uint256) { require(_glpAmount > 0, "RewardRouter: invalid _glpAmount"); address account = msg.sender; IRewardTracker(stakedGlpTracker).unstakeForAccount(account, feeGlpTracker, _glpAmount, account); IRewardTracker(feeGlpTracker).unstakeForAccount(account, glp, _glpAmount, account); uint256 amountOut = IGlpManager(glpManager).removeLiquidityForAccount(account, _tokenOut, _glpAmount, _minOut, _receiver); emit UnstakeGlp(account, _glpAmount); return amountOut; } function unstakeAndRedeemGlpETH(uint256 _glpAmount, uint256 _minOut, address payable _receiver) external nonReentrant returns (uint256) { require(_glpAmount > 0, "RewardRouter: invalid _glpAmount"); address account = msg.sender; IRewardTracker(stakedGlpTracker).unstakeForAccount(account, feeGlpTracker, _glpAmount, account); IRewardTracker(feeGlpTracker).unstakeForAccount(account, glp, _glpAmount, account); uint256 amountOut = IGlpManager(glpManager).removeLiquidityForAccount(account, weth, _glpAmount, _minOut, address(this)); IWETH(weth).withdraw(amountOut); _receiver.sendValue(amountOut); emit UnstakeGlp(account, _glpAmount); return amountOut; } function claim() external nonReentrant { address account = msg.sender; IRewardTracker(feeGmxTracker).claimForAccount(account, account); IRewardTracker(feeGlpTracker).claimForAccount(account, account); IRewardTracker(stakedGmxTracker).claimForAccount(account, account); IRewardTracker(stakedGlpTracker).claimForAccount(account, account); } function claimEsGmx() external nonReentrant { address account = msg.sender; IRewardTracker(stakedGmxTracker).claimForAccount(account, account); IRewardTracker(stakedGlpTracker).claimForAccount(account, account); } function claimFees() external nonReentrant { address account = msg.sender; IRewardTracker(feeGmxTracker).claimForAccount(account, account); IRewardTracker(feeGlpTracker).claimForAccount(account, account); } function compound() external nonReentrant { _compound(msg.sender); } function compoundForAccount(address _account) external nonReentrant onlyGov { _compound(_account); } function batchCompoundForAccounts(address[] memory _accounts) external nonReentrant onlyGov { for (uint256 i = 0; i < _accounts.length; i++) { _compound(_accounts[i]); } } function _compound(address _account) private { _compoundGmx(_account); _compoundGlp(_account); } function _compoundGmx(address _account) private { uint256 esGmxAmount = IRewardTracker(stakedGmxTracker).claimForAccount(_account, _account); if (esGmxAmount > 0) { _stakeGmx(_account, _account, esGmx, esGmxAmount); } uint256 bnGmxAmount = IRewardTracker(bonusGmxTracker).claimForAccount(_account, _account); if (bnGmxAmount > 0) { IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bnGmx, bnGmxAmount); } } function _compoundGlp(address _account) private { uint256 esGmxAmount = IRewardTracker(stakedGlpTracker).claimForAccount(_account, _account); if (esGmxAmount > 0) { _stakeGmx(_account, _account, esGmx, esGmxAmount); } } function _stakeGmx(address _fundingAccount, address _account, address _token, uint256 _amount) private { require(_amount > 0, "RewardRouter: invalid _amount"); IRewardTracker(stakedGmxTracker).stakeForAccount(_fundingAccount, _account, _token, _amount); IRewardTracker(bonusGmxTracker).stakeForAccount(_account, _account, stakedGmxTracker, _amount); IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bonusGmxTracker, _amount); emit StakeGmx(_account, _amount); } function _unstakeGmx(address _account, address _token, uint256 _amount) private { require(_amount > 0, "RewardRouter: invalid _amount"); uint256 balance = IRewardTracker(stakedGmxTracker).stakedAmounts(_account); IRewardTracker(feeGmxTracker).unstakeForAccount(_account, bonusGmxTracker, _amount, _account); IRewardTracker(bonusGmxTracker).unstakeForAccount(_account, stakedGmxTracker, _amount, _account); IRewardTracker(stakedGmxTracker).unstakeForAccount(_account, _token, _amount, _account); uint256 bnGmxAmount = IRewardTracker(bonusGmxTracker).claimForAccount(_account, _account); if (bnGmxAmount > 0) { IRewardTracker(feeGmxTracker).stakeForAccount(_account, _account, bnGmx, bnGmxAmount); } uint256 stakedBnGmx = IRewardTracker(feeGmxTracker).depositBalances(_account, bnGmx); if (stakedBnGmx > 0) { uint256 reductionAmount = stakedBnGmx.mul(_amount).div(balance); IRewardTracker(feeGmxTracker).unstakeForAccount(_account, bnGmx, reductionAmount, _account); IMintable(bnGmx).burn(_account, reductionAmount); } emit UnstakeGmx(_account, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../../GSN/Context.sol"; import "./IERC721.sol"; import "./IERC721Metadata.sol"; import "./IERC721Enumerable.sol"; import "./IERC721Receiver.sol"; import "../../introspection/ERC165.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; import "../../utils/EnumerableSet.sol"; import "../../utils/EnumerableMap.sol"; import "../../utils/Strings.sol"; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable { using SafeMath for uint256; using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableMap for EnumerableMap.UintToAddressMap; using Strings for uint256; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; // Mapping from holder address to their (enumerable) set of owned tokens mapping (address => EnumerableSet.UintSet) private _holderTokens; // Enumerable mapping from token ids to their owners EnumerableMap.UintToAddressMap private _tokenOwners; // Mapping from token ID to approved address mapping (uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping (address => mapping (address => bool)) private _operatorApprovals; // Token name string private _name; // Token symbol string private _symbol; // Optional mapping for token URIs mapping (uint256 => string) private _tokenURIs; // Base URI string private _baseURI; /* * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde * * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd */ bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; /* * bytes4(keccak256('name()')) == 0x06fdde03 * bytes4(keccak256('symbol()')) == 0x95d89b41 * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd * * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f */ bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; /* * bytes4(keccak256('totalSupply()')) == 0x18160ddd * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 * * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 */ bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); _registerInterface(_INTERFACE_ID_ERC721_METADATA); _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } function mint(address to, uint256 tokenId) public { _mint(to, tokenId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _holderTokens[owner].length(); } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view override returns (address) { return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token"); } /** * @dev See {IERC721Metadata-name}. */ function name() public view override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory _tokenURI = _tokenURIs[tokenId]; // If there is no base URI, return the token URI. if (bytes(_baseURI).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). if (bytes(_tokenURI).length > 0) { return string(abi.encodePacked(_baseURI, _tokenURI)); } // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI. return string(abi.encodePacked(_baseURI, tokenId.toString())); } /** * @dev Returns the base URI set via {_setBaseURI}. This will be * automatically added as a prefix in {tokenURI} to each token's URI, or * to the token ID if no specific URI is set for that token ID. */ function baseURI() public view returns (string memory) { return _baseURI; } /** * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. */ function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) { return _holderTokens[owner].at(index); } /** * @dev See {IERC721Enumerable-totalSupply}. */ function totalSupply() public view override returns (uint256) { // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds return _tokenOwners.length(); } /** * @dev See {IERC721Enumerable-tokenByIndex}. */ function tokenByIndex(uint256 index) public view override returns (uint256) { (uint256 tokenId, ) = _tokenOwners.at(index); return tokenId; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { require(operator != _msgSender(), "ERC721: approve to caller"); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view returns (bool) { return _tokenOwners.contains(tokenId); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: d* * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { _mint(to, tokenId); require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ownerOf(tokenId); _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); // Clear metadata (if any) if (bytes(_tokenURIs[tokenId]).length != 0) { delete _tokenURIs[tokenId]; } _holderTokens[owner].remove(tokenId); _tokenOwners.remove(tokenId); emit Transfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal virtual { require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _holderTokens[from].remove(tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(from, to, tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } /** * @dev Internal function to set the base URI for all token IDs. It is * automatically added as a prefix to the value returned in {tokenURI}, * or to the token ID if {tokenURI} is empty. */ function _setBaseURI(string memory baseURI_) internal virtual { _baseURI = baseURI_; } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) { if (!to.isContract()) { return true; } bytes memory returndata = to.functionCall(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, _msgSender(), from, tokenId, _data ), "ERC721: transfer to non ERC721Receiver implementer"); bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } function _approve(address to, uint256 tokenId) private { _tokenApprovals[tokenId] = to; emit Approval(ownerOf(tokenId), to, tokenId); } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` cannot be the zero address. * - `to` cannot be the zero address. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; /* * @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 GSN 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 Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.2; import "../../introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.2; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.2; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Enumerable is IERC721 { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ contract ERC165 is IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () internal { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See {IERC165-supportsInterface}. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) public view override returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See {IERC165-supportsInterface}. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal virtual { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256` * (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(value))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(value))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(value))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint256(_at(set._inner, index))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Library for managing an enumerable variant of Solidity's * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] * type. * * Maps have the following properties: * * - Entries are added, removed, and checked for existence in constant time * (O(1)). * - Entries are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableMap for EnumerableMap.UintToAddressMap; * * // Declare a set state variable * EnumerableMap.UintToAddressMap private myMap; * } * ``` * * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are * supported. */ library EnumerableMap { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Map type with // bytes32 keys and values. // The Map implementation uses private functions, and user-facing // implementations (such as Uint256ToAddressMap) are just wrappers around // the underlying Map. // This means that we can only create new EnumerableMaps for types that fit // in bytes32. struct MapEntry { bytes32 _key; bytes32 _value; } struct Map { // Storage of map keys and values MapEntry[] _entries; // Position of the entry defined by a key in the `entries` array, plus 1 // because index 0 means a key is not in the map. mapping (bytes32 => uint256) _indexes; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex == 0) { // Equivalent to !contains(map, key) map._entries.push(MapEntry({ _key: key, _value: value })); // The entry is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value map._indexes[key] = map._entries.length; return true; } else { map._entries[keyIndex - 1]._value = value; return false; } } /** * @dev Removes a key-value pair from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function _remove(Map storage map, bytes32 key) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex != 0) { // Equivalent to contains(map, key) // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one // in the array, and then remove the last entry (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = keyIndex - 1; uint256 lastIndex = map._entries.length - 1; // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. MapEntry storage lastEntry = map._entries[lastIndex]; // Move the last entry to the index where the entry to delete is map._entries[toDeleteIndex] = lastEntry; // Update the index for the moved entry map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved entry was stored map._entries.pop(); // Delete the index for the deleted slot delete map._indexes[key]; return true; } else { return false; } } /** * @dev Returns true if the key is in the map. O(1). */ function _contains(Map storage map, bytes32 key) private view returns (bool) { return map._indexes[key] != 0; } /** * @dev Returns the number of key-value pairs in the map. O(1). */ function _length(Map storage map) private view returns (uint256) { return map._entries.length; } /** * @dev Returns the key-value pair stored at position `index` in the map. O(1). * * Note that there are no guarantees on the ordering of entries inside the * array, and it may change when more entries are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { require(map._entries.length > index, "EnumerableMap: index out of bounds"); MapEntry storage entry = map._entries[index]; return (entry._key, entry._value); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function _get(Map storage map, bytes32 key) private view returns (bytes32) { return _get(map, key, "EnumerableMap: nonexistent key"); } /** * @dev Same as {_get}, with a custom error message when `key` is not in the map. */ function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) { uint256 keyIndex = map._indexes[key]; require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key) return map._entries[keyIndex - 1]._value; // All indexes are 1-based } // UintToAddressMap struct UintToAddressMap { Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { return _set(map._inner, bytes32(key), bytes32(uint256(value))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { return _remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { return _contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToAddressMap storage map) internal view returns (uint256) { return _length(map._inner); } /** * @dev Returns the element stored at position `index` in the set. O(1). * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { (bytes32 key, bytes32 value) = _at(map._inner, index); return (uint256(key), address(uint256(value))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { return address(uint256(_get(map._inner, bytes32(key)))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. */ function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) { return address(uint256(_get(map._inner, bytes32(key), errorMessage))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev String operations. */ library Strings { /** * @dev Converts a `uint256` to its ASCII `string` representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); uint256 index = digits - 1; temp = value; while (temp != 0) { buffer[index--] = byte(uint8(48 + temp % 10)); temp /= 10; } return string(buffer); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/ERC721/IERC721.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../peripherals/interfaces/ITimelock.sol"; contract TokenManager is ReentrancyGuard { using SafeMath for uint256; bool public isInitialized; uint256 public actionsNonce; uint256 public minAuthorizations; address public admin; address[] public signers; mapping (address => bool) public isSigner; mapping (bytes32 => bool) public pendingActions; mapping (address => mapping (bytes32 => bool)) public signedActions; event SignalApprove(address token, address spender, uint256 amount, bytes32 action, uint256 nonce); event SignalApproveNFT(address token, address spender, uint256 tokenId, bytes32 action, uint256 nonce); event SignalApproveNFTs(address token, address spender, uint256[] tokenIds, bytes32 action, uint256 nonce); event SignalSetAdmin(address target, address admin, bytes32 action, uint256 nonce); event SignalSetGov(address timelock, address target, address gov, bytes32 action, uint256 nonce); event SignalPendingAction(bytes32 action, uint256 nonce); event SignAction(bytes32 action, uint256 nonce); event ClearAction(bytes32 action, uint256 nonce); constructor(uint256 _minAuthorizations) public { admin = msg.sender; minAuthorizations = _minAuthorizations; } modifier onlyAdmin() { require(msg.sender == admin, "TokenManager: forbidden"); _; } modifier onlySigner() { require(isSigner[msg.sender], "TokenManager: forbidden"); _; } function initialize(address[] memory _signers) public virtual onlyAdmin { require(!isInitialized, "TokenManager: already initialized"); isInitialized = true; signers = _signers; for (uint256 i = 0; i < _signers.length; i++) { address signer = _signers[i]; isSigner[signer] = true; } } function signersLength() public view returns (uint256) { return signers.length; } function signalApprove(address _token, address _spender, uint256 _amount) external nonReentrant onlyAdmin { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, nonce)); _setPendingAction(action, nonce); emit SignalApprove(_token, _spender, _amount, action, nonce); } function signApprove(address _token, address _spender, uint256 _amount, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "TokenManager: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function approve(address _token, address _spender, uint256 _amount, uint256 _nonce) external nonReentrant onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, _nonce)); _validateAction(action); _validateAuthorization(action); IERC20(_token).approve(_spender, _amount); _clearAction(action, _nonce); } function signalApproveNFT(address _token, address _spender, uint256 _tokenId) external nonReentrant onlyAdmin { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("approveNFT", _token, _spender, _tokenId, nonce)); _setPendingAction(action, nonce); emit SignalApproveNFT(_token, _spender, _tokenId, action, nonce); } function signApproveNFT(address _token, address _spender, uint256 _tokenId, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("approveNFT", _token, _spender, _tokenId, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "TokenManager: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function approveNFT(address _token, address _spender, uint256 _tokenId, uint256 _nonce) external nonReentrant onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approveNFT", _token, _spender, _tokenId, _nonce)); _validateAction(action); _validateAuthorization(action); IERC721(_token).approve(_spender, _tokenId); _clearAction(action, _nonce); } function signalApproveNFTs(address _token, address _spender, uint256[] memory _tokenIds) external nonReentrant onlyAdmin { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("approveNFTs", _token, _spender, _tokenIds, nonce)); _setPendingAction(action, nonce); emit SignalApproveNFTs(_token, _spender, _tokenIds, action, nonce); } function signApproveNFTs(address _token, address _spender, uint256[] memory _tokenIds, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("approveNFTs", _token, _spender, _tokenIds, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "TokenManager: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function approveNFTs(address _token, address _spender, uint256[] memory _tokenIds, uint256 _nonce) external nonReentrant onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approveNFTs", _token, _spender, _tokenIds, _nonce)); _validateAction(action); _validateAuthorization(action); for (uint256 i = 0 ; i < _tokenIds.length; i++) { IERC721(_token).approve(_spender, _tokenIds[i]); } _clearAction(action, _nonce); } function receiveNFTs(address _token, address _sender, uint256[] memory _tokenIds) external nonReentrant onlyAdmin { for (uint256 i = 0 ; i < _tokenIds.length; i++) { IERC721(_token).transferFrom(_sender, address(this), _tokenIds[i]); } } function signalSetAdmin(address _target, address _admin) external nonReentrant onlySigner { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("setAdmin", _target, _admin, nonce)); _setPendingAction(action, nonce); signedActions[msg.sender][action] = true; emit SignalSetAdmin(_target, _admin, action, nonce); } function signSetAdmin(address _target, address _admin, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("setAdmin", _target, _admin, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "TokenManager: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function setAdmin(address _target, address _admin, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("setAdmin", _target, _admin, _nonce)); _validateAction(action); _validateAuthorization(action); ITimelock(_target).setAdmin(_admin); _clearAction(action, _nonce); } function signalSetGov(address _timelock, address _target, address _gov) external nonReentrant onlyAdmin { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("signalSetGov", _timelock, _target, _gov, nonce)); _setPendingAction(action, nonce); signedActions[msg.sender][action] = true; emit SignalSetGov(_timelock, _target, _gov, action, nonce); } function signSetGov(address _timelock, address _target, address _gov, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("signalSetGov", _timelock, _target, _gov, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "TokenManager: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function setGov(address _timelock, address _target, address _gov, uint256 _nonce) external nonReentrant onlyAdmin { bytes32 action = keccak256(abi.encodePacked("signalSetGov", _timelock, _target, _gov, _nonce)); _validateAction(action); _validateAuthorization(action); ITimelock(_timelock).signalSetGov(_target, _gov); _clearAction(action, _nonce); } function _setPendingAction(bytes32 _action, uint256 _nonce) private { pendingActions[_action] = true; emit SignalPendingAction(_action, _nonce); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action], "TokenManager: action not signalled"); } function _validateAuthorization(bytes32 _action) private view { uint256 count = 0; for (uint256 i = 0; i < signers.length; i++) { address signer = signers[i]; if (signedActions[signer][_action]) { count++; } } if (count == 0) { revert("TokenManager: action not authorized"); } require(count >= minAuthorizations, "TokenManager: insufficient authorization"); } function _clearAction(bytes32 _action, uint256 _nonce) private { require(pendingActions[_action], "TokenManager: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action, _nonce); } }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "../tokens/interfaces/IMintable.sol"; import "../access/TokenManager.sol"; contract GmxFloor is ReentrancyGuard, TokenManager { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant BURN_BASIS_POINTS = 9000; address public gmx; address public reserveToken; uint256 public backedSupply; uint256 public baseMintPrice; uint256 public mintMultiplier; uint256 public mintedSupply; uint256 public multiplierPrecision; mapping (address => bool) public isHandler; modifier onlyHandler() { require(isHandler[msg.sender], "GmxFloor: forbidden"); _; } constructor( address _gmx, address _reserveToken, uint256 _backedSupply, uint256 _baseMintPrice, uint256 _mintMultiplier, uint256 _multiplierPrecision, uint256 _minAuthorizations ) public TokenManager(_minAuthorizations) { gmx = _gmx; reserveToken = _reserveToken; backedSupply = _backedSupply; baseMintPrice = _baseMintPrice; mintMultiplier = _mintMultiplier; multiplierPrecision = _multiplierPrecision; } function initialize(address[] memory _signers) public override onlyAdmin { TokenManager.initialize(_signers); } function setHandler(address _handler, bool _isHandler) public onlyAdmin { isHandler[_handler] = _isHandler; } function setBackedSupply(uint256 _backedSupply) public onlyAdmin { require(_backedSupply > backedSupply, "GmxFloor: invalid _backedSupply"); backedSupply = _backedSupply; } function setMintMultiplier(uint256 _mintMultiplier) public onlyAdmin { require(_mintMultiplier > mintMultiplier, "GmxFloor: invalid _mintMultiplier"); mintMultiplier = _mintMultiplier; } // mint refers to increasing the circulating supply // the GMX tokens to be transferred out must be pre-transferred into this contract function mint(uint256 _amount, uint256 _maxCost, address _receiver) public onlyHandler nonReentrant returns (uint256) { require(_amount > 0, "GmxFloor: invalid _amount"); uint256 currentMintPrice = getMintPrice(); uint256 nextMintPrice = currentMintPrice.add(_amount.mul(mintMultiplier).div(multiplierPrecision)); uint256 averageMintPrice = currentMintPrice.add(nextMintPrice).div(2); uint256 cost = _amount.mul(averageMintPrice).div(PRICE_PRECISION); require(cost <= _maxCost, "GmxFloor: _maxCost exceeded"); mintedSupply = mintedSupply.add(_amount); backedSupply = backedSupply.add(_amount); IERC20(reserveToken).safeTransferFrom(msg.sender, address(this), cost); IERC20(gmx).transfer(_receiver, _amount); return cost; } function burn(uint256 _amount, uint256 _minOut, address _receiver) public onlyHandler nonReentrant returns (uint256) { require(_amount > 0, "GmxFloor: invalid _amount"); uint256 amountOut = getBurnAmountOut(_amount); require(amountOut >= _minOut, "GmxFloor: insufficient amountOut"); backedSupply = backedSupply.sub(_amount); IMintable(gmx).burn(msg.sender, _amount); IERC20(reserveToken).safeTransfer(_receiver, amountOut); return amountOut; } function getMintPrice() public view returns (uint256) { return baseMintPrice.add(mintedSupply.mul(mintMultiplier).div(multiplierPrecision)); } function getBurnAmountOut(uint256 _amount) public view returns (uint256) { uint256 balance = IERC20(reserveToken).balanceOf(address(this)); return _amount.mul(balance).div(backedSupply).mul(BURN_BASIS_POINTS).div(BASIS_POINTS_DIVISOR); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../GSN/Context.sol"; import "./IERC20.sol"; import "../math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../tokens/interfaces/IWETH.sol"; import "../libraries/token/SafeERC20.sol"; import "../libraries/utils/Address.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IRouter.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IOrderBook.sol"; contract OrderBook is ReentrancyGuard, IOrderBook { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address payable; uint256 public constant PRICE_PRECISION = 1e30; uint256 public constant USDG_PRECISION = 1e18; struct IncreaseOrder { address account; address purchaseToken; uint256 purchaseTokenAmount; address collateralToken; address indexToken; uint256 sizeDelta; bool isLong; uint256 triggerPrice; bool triggerAboveThreshold; uint256 executionFee; } struct DecreaseOrder { address account; address collateralToken; uint256 collateralDelta; address indexToken; uint256 sizeDelta; bool isLong; uint256 triggerPrice; bool triggerAboveThreshold; uint256 executionFee; } struct SwapOrder { address account; address[] path; uint256 amountIn; uint256 minOut; uint256 triggerRatio; bool triggerAboveThreshold; bool shouldUnwrap; uint256 executionFee; } mapping (address => mapping(uint256 => IncreaseOrder)) public increaseOrders; mapping (address => uint256) public increaseOrdersIndex; mapping (address => mapping(uint256 => DecreaseOrder)) public decreaseOrders; mapping (address => uint256) public decreaseOrdersIndex; mapping (address => mapping(uint256 => SwapOrder)) public swapOrders; mapping (address => uint256) public swapOrdersIndex; address public gov; address public weth; address public usdg; address public router; address public vault; uint256 public minExecutionFee; uint256 public minPurchaseTokenAmountUsd; bool public isInitialized = false; event CreateIncreaseOrder( address indexed account, uint256 orderIndex, address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); event CancelIncreaseOrder( address indexed account, uint256 orderIndex, address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); event ExecuteIncreaseOrder( address indexed account, uint256 orderIndex, address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee, uint256 executionPrice ); event UpdateIncreaseOrder( address indexed account, uint256 orderIndex, address collateralToken, address indexToken, bool isLong, uint256 sizeDelta, uint256 triggerPrice, bool triggerAboveThreshold ); event CreateDecreaseOrder( address indexed account, uint256 orderIndex, address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); event CancelDecreaseOrder( address indexed account, uint256 orderIndex, address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ); event ExecuteDecreaseOrder( address indexed account, uint256 orderIndex, address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee, uint256 executionPrice ); event UpdateDecreaseOrder( address indexed account, uint256 orderIndex, address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold ); event CreateSwapOrder( address indexed account, uint256 orderIndex, address[] path, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ); event CancelSwapOrder( address indexed account, uint256 orderIndex, address[] path, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ); event UpdateSwapOrder( address indexed account, uint256 ordexIndex, address[] path, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ); event ExecuteSwapOrder( address indexed account, uint256 orderIndex, address[] path, uint256 amountIn, uint256 minOut, uint256 amountOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ); event Initialize( address router, address vault, address weth, address usdg, uint256 minExecutionFee, uint256 minPurchaseTokenAmountUsd ); event UpdateMinExecutionFee(uint256 minExecutionFee); event UpdateMinPurchaseTokenAmountUsd(uint256 minPurchaseTokenAmountUsd); event UpdateGov(address gov); modifier onlyGov() { require(msg.sender == gov, "OrderBook: forbidden"); _; } constructor() public { gov = msg.sender; } function initialize( address _router, address _vault, address _weth, address _usdg, uint256 _minExecutionFee, uint256 _minPurchaseTokenAmountUsd ) external onlyGov { require(!isInitialized, "OrderBook: already initialized"); isInitialized = true; router = _router; vault = _vault; weth = _weth; usdg = _usdg; minExecutionFee = _minExecutionFee; minPurchaseTokenAmountUsd = _minPurchaseTokenAmountUsd; emit Initialize(_router, _vault, _weth, _usdg, _minExecutionFee, _minPurchaseTokenAmountUsd); } receive() external payable { require(msg.sender == weth, "OrderBook: invalid sender"); } function setMinExecutionFee(uint256 _minExecutionFee) external onlyGov { minExecutionFee = _minExecutionFee; emit UpdateMinExecutionFee(_minExecutionFee); } function setMinPurchaseTokenAmountUsd(uint256 _minPurchaseTokenAmountUsd) external onlyGov { minPurchaseTokenAmountUsd = _minPurchaseTokenAmountUsd; emit UpdateMinPurchaseTokenAmountUsd(_minPurchaseTokenAmountUsd); } function setGov(address _gov) external onlyGov { gov = _gov; emit UpdateGov(_gov); } function getSwapOrder(address _account, uint256 _orderIndex) override public view returns ( address path0, address path1, address path2, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, uint256 executionFee ) { SwapOrder memory order = swapOrders[_account][_orderIndex]; return ( order.path.length > 0 ? order.path[0] : address(0), order.path.length > 1 ? order.path[1] : address(0), order.path.length > 2 ? order.path[2] : address(0), order.amountIn, order.minOut, order.triggerRatio, order.triggerAboveThreshold, order.shouldUnwrap, order.executionFee ); } function createSwapOrder( address[] memory _path, uint256 _amountIn, uint256 _minOut, uint256 _triggerRatio, // tokenB / tokenA bool _triggerAboveThreshold, uint256 _executionFee, bool _shouldWrap, bool _shouldUnwrap ) external payable nonReentrant { require(_path.length == 2 || _path.length == 3, "OrderBook: invalid _path.length"); require(_path[0] != _path[_path.length - 1], "OrderBook: invalid _path"); require(_amountIn > 0, "OrderBook: invalid _amountIn"); require(_executionFee >= minExecutionFee, "OrderBook: insufficient execution fee"); // always need this call because of mandatory executionFee user has to transfer in ETH _transferInETH(); if (_shouldWrap) { require(_path[0] == weth, "OrderBook: only weth could be wrapped"); require(msg.value == _executionFee.add(_amountIn), "OrderBook: incorrect value transferred"); } else { require(msg.value == _executionFee, "OrderBook: incorrect execution fee transferred"); IRouter(router).pluginTransfer(_path[0], msg.sender, address(this), _amountIn); } _createSwapOrder(msg.sender, _path, _amountIn, _minOut, _triggerRatio, _triggerAboveThreshold, _shouldUnwrap, _executionFee); } function _createSwapOrder( address _account, address[] memory _path, uint256 _amountIn, uint256 _minOut, uint256 _triggerRatio, bool _triggerAboveThreshold, bool _shouldUnwrap, uint256 _executionFee ) private { uint256 _orderIndex = swapOrdersIndex[_account]; SwapOrder memory order = SwapOrder( _account, _path, _amountIn, _minOut, _triggerRatio, _triggerAboveThreshold, _shouldUnwrap, _executionFee ); swapOrdersIndex[_account] = _orderIndex.add(1); swapOrders[_account][_orderIndex] = order; emit CreateSwapOrder( _account, _orderIndex, _path, _amountIn, _minOut, _triggerRatio, _triggerAboveThreshold, _shouldUnwrap, _executionFee ); } function cancelMultiple( uint256[] memory _swapOrderIndexes, uint256[] memory _increaseOrderIndexes, uint256[] memory _decreaseOrderIndexes ) external { for (uint256 i = 0; i < _swapOrderIndexes.length; i++) { cancelSwapOrder(_swapOrderIndexes[i]); } for (uint256 i = 0; i < _increaseOrderIndexes.length; i++) { cancelIncreaseOrder(_increaseOrderIndexes[i]); } for (uint256 i = 0; i < _decreaseOrderIndexes.length; i++) { cancelDecreaseOrder(_decreaseOrderIndexes[i]); } } function cancelSwapOrder(uint256 _orderIndex) public nonReentrant { SwapOrder memory order = swapOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); delete swapOrders[msg.sender][_orderIndex]; if (order.path[0] == weth) { _transferOutETH(order.executionFee.add(order.amountIn), msg.sender); } else { IERC20(order.path[0]).safeTransfer(msg.sender, order.amountIn); _transferOutETH(order.executionFee, msg.sender); } emit CancelSwapOrder( msg.sender, _orderIndex, order.path, order.amountIn, order.minOut, order.triggerRatio, order.triggerAboveThreshold, order.shouldUnwrap, order.executionFee ); } function getUsdgMinPrice(address _otherToken) public view returns (uint256) { // USDG_PRECISION is the same as 1 USDG uint256 redemptionAmount = IVault(vault).getRedemptionAmount(_otherToken, USDG_PRECISION); uint256 otherTokenPrice = IVault(vault).getMinPrice(_otherToken); uint256 otherTokenDecimals = IVault(vault).tokenDecimals(_otherToken); return redemptionAmount.mul(otherTokenPrice).div(10 ** otherTokenDecimals); } function validateSwapOrderPriceWithTriggerAboveThreshold( address[] memory _path, uint256 _triggerRatio ) public view returns (bool) { require(_path.length == 2 || _path.length == 3, "OrderBook: invalid _path.length"); // limit orders don't need this validation because minOut is enough // so this validation handles scenarios for stop orders only // when a user wants to swap when a price of tokenB increases relative to tokenA address tokenA = _path[0]; address tokenB = _path[_path.length - 1]; uint256 tokenAPrice; uint256 tokenBPrice; // 1. USDG doesn't have a price feed so we need to calculate it based on redepmtion amount of a specific token // That's why USDG price in USD can vary depending on the redepmtion token // 2. In complex scenarios with path=[USDG, BNB, BTC] we need to know how much BNB we'll get for provided USDG // to know how much BTC will be received // That's why in such scenario BNB should be used to determine price of USDG if (tokenA == usdg) { // with both _path.length == 2 or 3 we need usdg price against _path[1] tokenAPrice = getUsdgMinPrice(_path[1]); } else { tokenAPrice = IVault(vault).getMinPrice(tokenA); } if (tokenB == usdg) { tokenBPrice = PRICE_PRECISION; } else { tokenBPrice = IVault(vault).getMaxPrice(tokenB); } uint256 currentRatio = tokenBPrice.mul(PRICE_PRECISION).div(tokenAPrice); bool isValid = currentRatio > _triggerRatio; return isValid; } function updateSwapOrder(uint256 _orderIndex, uint256 _minOut, uint256 _triggerRatio, bool _triggerAboveThreshold) external nonReentrant { SwapOrder storage order = swapOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); order.minOut = _minOut; order.triggerRatio = _triggerRatio; order.triggerAboveThreshold = _triggerAboveThreshold; emit UpdateSwapOrder( msg.sender, _orderIndex, order.path, order.amountIn, _minOut, _triggerRatio, _triggerAboveThreshold, order.shouldUnwrap, order.executionFee ); } function executeSwapOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) override external nonReentrant { SwapOrder memory order = swapOrders[_account][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); if (order.triggerAboveThreshold) { // gas optimisation // order.minAmount should prevent wrong price execution in case of simple limit order require( validateSwapOrderPriceWithTriggerAboveThreshold(order.path, order.triggerRatio), "OrderBook: invalid price for execution" ); } delete swapOrders[_account][_orderIndex]; IERC20(order.path[0]).safeTransfer(vault, order.amountIn); uint256 _amountOut; if (order.path[order.path.length - 1] == weth && order.shouldUnwrap) { _amountOut = _swap(order.path, order.minOut, address(this)); _transferOutETH(_amountOut, payable(order.account)); } else { _amountOut = _swap(order.path, order.minOut, order.account); } // pay executor _transferOutETH(order.executionFee, _feeReceiver); emit ExecuteSwapOrder( _account, _orderIndex, order.path, order.amountIn, order.minOut, _amountOut, order.triggerRatio, order.triggerAboveThreshold, order.shouldUnwrap, order.executionFee ); } function validatePositionOrderPrice( bool _triggerAboveThreshold, uint256 _triggerPrice, address _indexToken, bool _maximizePrice, bool _raise ) public view returns (uint256, bool) { uint256 currentPrice = _maximizePrice ? IVault(vault).getMaxPrice(_indexToken) : IVault(vault).getMinPrice(_indexToken); bool isPriceValid = _triggerAboveThreshold ? currentPrice > _triggerPrice : currentPrice < _triggerPrice; if (_raise) { require(isPriceValid, "OrderBook: invalid price for execution"); } return (currentPrice, isPriceValid); } function getDecreaseOrder(address _account, uint256 _orderIndex) override public view returns ( address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ) { DecreaseOrder memory order = decreaseOrders[_account][_orderIndex]; return ( order.collateralToken, order.collateralDelta, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee ); } function getIncreaseOrder(address _account, uint256 _orderIndex) override public view returns ( address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, uint256 executionFee ) { IncreaseOrder memory order = increaseOrders[_account][_orderIndex]; return ( order.purchaseToken, order.purchaseTokenAmount, order.collateralToken, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee ); } function createIncreaseOrder( address[] memory _path, uint256 _amountIn, address _indexToken, uint256 _minOut, uint256 _sizeDelta, address _collateralToken, bool _isLong, uint256 _triggerPrice, bool _triggerAboveThreshold, uint256 _executionFee, bool _shouldWrap ) external payable nonReentrant { // always need this call because of mandatory executionFee user has to transfer in ETH _transferInETH(); require(_executionFee >= minExecutionFee, "OrderBook: insufficient execution fee"); if (_shouldWrap) { require(_path[0] == weth, "OrderBook: only weth could be wrapped"); require(msg.value == _executionFee.add(_amountIn), "OrderBook: incorrect value transferred"); } else { require(msg.value == _executionFee, "OrderBook: incorrect execution fee transferred"); IRouter(router).pluginTransfer(_path[0], msg.sender, address(this), _amountIn); } address _purchaseToken = _path[_path.length - 1]; uint256 _purchaseTokenAmount; if (_path.length > 1) { require(_path[0] != _purchaseToken, "OrderBook: invalid _path"); IERC20(_path[0]).safeTransfer(vault, _amountIn); _purchaseTokenAmount = _swap(_path, _minOut, address(this)); } else { _purchaseTokenAmount = _amountIn; } { uint256 _purchaseTokenAmountUsd = IVault(vault).tokenToUsdMin(_purchaseToken, _purchaseTokenAmount); require(_purchaseTokenAmountUsd >= minPurchaseTokenAmountUsd, "OrderBook: insufficient collateral"); } _createIncreaseOrder( msg.sender, _purchaseToken, _purchaseTokenAmount, _collateralToken, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold, _executionFee ); } function _createIncreaseOrder( address _account, address _purchaseToken, uint256 _purchaseTokenAmount, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _triggerPrice, bool _triggerAboveThreshold, uint256 _executionFee ) private { uint256 _orderIndex = increaseOrdersIndex[msg.sender]; IncreaseOrder memory order = IncreaseOrder( _account, _purchaseToken, _purchaseTokenAmount, _collateralToken, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold, _executionFee ); increaseOrdersIndex[_account] = _orderIndex.add(1); increaseOrders[_account][_orderIndex] = order; emit CreateIncreaseOrder( _account, _orderIndex, _purchaseToken, _purchaseTokenAmount, _collateralToken, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold, _executionFee ); } function updateIncreaseOrder(uint256 _orderIndex, uint256 _sizeDelta, uint256 _triggerPrice, bool _triggerAboveThreshold) external nonReentrant { IncreaseOrder storage order = increaseOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); order.triggerPrice = _triggerPrice; order.triggerAboveThreshold = _triggerAboveThreshold; order.sizeDelta = _sizeDelta; emit UpdateIncreaseOrder( msg.sender, _orderIndex, order.collateralToken, order.indexToken, order.isLong, _sizeDelta, _triggerPrice, _triggerAboveThreshold ); } function cancelIncreaseOrder(uint256 _orderIndex) public nonReentrant { IncreaseOrder memory order = increaseOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); delete increaseOrders[msg.sender][_orderIndex]; if (order.purchaseToken == weth) { _transferOutETH(order.executionFee.add(order.purchaseTokenAmount), msg.sender); } else { IERC20(order.purchaseToken).safeTransfer(msg.sender, order.purchaseTokenAmount); _transferOutETH(order.executionFee, msg.sender); } emit CancelIncreaseOrder( order.account, _orderIndex, order.purchaseToken, order.purchaseTokenAmount, order.collateralToken, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee ); } function executeIncreaseOrder(address _address, uint256 _orderIndex, address payable _feeReceiver) override external nonReentrant { IncreaseOrder memory order = increaseOrders[_address][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); // increase long should use max price // increase short should use min price (uint256 currentPrice, ) = validatePositionOrderPrice( order.triggerAboveThreshold, order.triggerPrice, order.indexToken, order.isLong, true ); delete increaseOrders[_address][_orderIndex]; IERC20(order.purchaseToken).safeTransfer(vault, order.purchaseTokenAmount); if (order.purchaseToken != order.collateralToken) { address[] memory path = new address[](2); path[0] = order.purchaseToken; path[1] = order.collateralToken; uint256 amountOut = _swap(path, 0, address(this)); IERC20(order.collateralToken).safeTransfer(vault, amountOut); } IRouter(router).pluginIncreasePosition(order.account, order.collateralToken, order.indexToken, order.sizeDelta, order.isLong); // pay executor _transferOutETH(order.executionFee, _feeReceiver); emit ExecuteIncreaseOrder( order.account, _orderIndex, order.purchaseToken, order.purchaseTokenAmount, order.collateralToken, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee, currentPrice ); } function createDecreaseOrder( address _indexToken, uint256 _sizeDelta, address _collateralToken, uint256 _collateralDelta, bool _isLong, uint256 _triggerPrice, bool _triggerAboveThreshold ) external payable nonReentrant { _transferInETH(); require(msg.value > minExecutionFee, "OrderBook: insufficient execution fee"); _createDecreaseOrder( msg.sender, _collateralToken, _collateralDelta, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold ); } function _createDecreaseOrder( address _account, address _collateralToken, uint256 _collateralDelta, address _indexToken, uint256 _sizeDelta, bool _isLong, uint256 _triggerPrice, bool _triggerAboveThreshold ) private { uint256 _orderIndex = decreaseOrdersIndex[_account]; DecreaseOrder memory order = DecreaseOrder( _account, _collateralToken, _collateralDelta, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold, msg.value ); decreaseOrdersIndex[_account] = _orderIndex.add(1); decreaseOrders[_account][_orderIndex] = order; emit CreateDecreaseOrder( _account, _orderIndex, _collateralToken, _collateralDelta, _indexToken, _sizeDelta, _isLong, _triggerPrice, _triggerAboveThreshold, msg.value ); } function executeDecreaseOrder(address _address, uint256 _orderIndex, address payable _feeReceiver) override external nonReentrant { DecreaseOrder memory order = decreaseOrders[_address][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); // decrease long should use min price // decrease short should use max price (uint256 currentPrice, ) = validatePositionOrderPrice( order.triggerAboveThreshold, order.triggerPrice, order.indexToken, !order.isLong, true ); delete decreaseOrders[_address][_orderIndex]; uint256 amountOut = IRouter(router).pluginDecreasePosition( order.account, order.collateralToken, order.indexToken, order.collateralDelta, order.sizeDelta, order.isLong, address(this) ); // transfer released collateral to user if (order.collateralToken == weth) { _transferOutETH(amountOut, payable(order.account)); } else { IERC20(order.collateralToken).safeTransfer(order.account, amountOut); } // pay executor _transferOutETH(order.executionFee, _feeReceiver); emit ExecuteDecreaseOrder( order.account, _orderIndex, order.collateralToken, order.collateralDelta, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee, currentPrice ); } function cancelDecreaseOrder(uint256 _orderIndex) public nonReentrant { DecreaseOrder memory order = decreaseOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); delete decreaseOrders[msg.sender][_orderIndex]; _transferOutETH(order.executionFee, msg.sender); emit CancelDecreaseOrder( order.account, _orderIndex, order.collateralToken, order.collateralDelta, order.indexToken, order.sizeDelta, order.isLong, order.triggerPrice, order.triggerAboveThreshold, order.executionFee ); } function updateDecreaseOrder( uint256 _orderIndex, uint256 _collateralDelta, uint256 _sizeDelta, uint256 _triggerPrice, bool _triggerAboveThreshold ) external nonReentrant { DecreaseOrder storage order = decreaseOrders[msg.sender][_orderIndex]; require(order.account != address(0), "OrderBook: non-existent order"); order.triggerPrice = _triggerPrice; order.triggerAboveThreshold = _triggerAboveThreshold; order.sizeDelta = _sizeDelta; order.collateralDelta = _collateralDelta; emit UpdateDecreaseOrder( msg.sender, _orderIndex, order.collateralToken, _collateralDelta, order.indexToken, _sizeDelta, order.isLong, _triggerPrice, _triggerAboveThreshold ); } function _transferInETH() private { if (msg.value != 0) { IWETH(weth).deposit{value: msg.value}(); } } function _transferOutETH(uint256 _amountOut, address payable _receiver) private { IWETH(weth).withdraw(_amountOut); _receiver.sendValue(_amountOut); } function _swap(address[] memory _path, uint256 _minOut, address _receiver) private returns (uint256) { if (_path.length == 2) { return _vaultSwap(_path[0], _path[1], _minOut, _receiver); } if (_path.length == 3) { uint256 midOut = _vaultSwap(_path[0], _path[1], 0, address(this)); IERC20(_path[1]).safeTransfer(vault, midOut); return _vaultSwap(_path[1], _path[2], _minOut, _receiver); } revert("OrderBook: invalid _path.length"); } function _vaultSwap(address _tokenIn, address _tokenOut, uint256 _minOut, address _receiver) private returns (uint256) { uint256 amountOut; if (_tokenOut == usdg) { // buyUSDG amountOut = IVault(vault).buyUSDG(_tokenIn, _receiver); } else if (_tokenIn == usdg) { // sellUSDG amountOut = IVault(vault).sellUSDG(_tokenOut, _receiver); } else { // swap amountOut = IVault(vault).swap(_tokenIn, _tokenOut, _receiver); } require(amountOut >= _minOut, "OrderBook: insufficient amountOut"); return amountOut; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../core/interfaces/IOrderBook.sol"; contract OrderBookReader { using SafeMath for uint256; struct Vars { uint256 i; uint256 index; address account; uint256 uintLength; uint256 addressLength; } function getIncreaseOrders( address payable _orderBookAddress, address _account, uint256[] memory _indices ) external view returns (uint256[] memory, address[] memory) { Vars memory vars = Vars(0, 0, _account, 5, 3); uint256[] memory uintProps = new uint256[](vars.uintLength * _indices.length); address[] memory addressProps = new address[](vars.addressLength * _indices.length); IOrderBook orderBook = IOrderBook(_orderBookAddress); while (vars.i < _indices.length) { vars.index = _indices[vars.i]; ( address purchaseToken, uint256 purchaseTokenAmount, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, // uint256 executionFee ) = orderBook.getIncreaseOrder(vars.account, vars.index); uintProps[vars.i * vars.uintLength] = uint256(purchaseTokenAmount); uintProps[vars.i * vars.uintLength + 1] = uint256(sizeDelta); uintProps[vars.i * vars.uintLength + 2] = uint256(isLong ? 1 : 0); uintProps[vars.i * vars.uintLength + 3] = uint256(triggerPrice); uintProps[vars.i * vars.uintLength + 4] = uint256(triggerAboveThreshold ? 1 : 0); addressProps[vars.i * vars.addressLength] = (purchaseToken); addressProps[vars.i * vars.addressLength + 1] = (collateralToken); addressProps[vars.i * vars.addressLength + 2] = (indexToken); vars.i++; } return (uintProps, addressProps); } function getDecreaseOrders( address payable _orderBookAddress, address _account, uint256[] memory _indices ) external view returns (uint256[] memory, address[] memory) { Vars memory vars = Vars(0, 0, _account, 5, 2); uint256[] memory uintProps = new uint256[](vars.uintLength * _indices.length); address[] memory addressProps = new address[](vars.addressLength * _indices.length); IOrderBook orderBook = IOrderBook(_orderBookAddress); while (vars.i < _indices.length) { vars.index = _indices[vars.i]; ( address collateralToken, uint256 collateralDelta, address indexToken, uint256 sizeDelta, bool isLong, uint256 triggerPrice, bool triggerAboveThreshold, // uint256 executionFee ) = orderBook.getDecreaseOrder(vars.account, vars.index); uintProps[vars.i * vars.uintLength] = uint256(collateralDelta); uintProps[vars.i * vars.uintLength + 1] = uint256(sizeDelta); uintProps[vars.i * vars.uintLength + 2] = uint256(isLong ? 1 : 0); uintProps[vars.i * vars.uintLength + 3] = uint256(triggerPrice); uintProps[vars.i * vars.uintLength + 4] = uint256(triggerAboveThreshold ? 1 : 0); addressProps[vars.i * vars.addressLength] = (collateralToken); addressProps[vars.i * vars.addressLength + 1] = (indexToken); vars.i++; } return (uintProps, addressProps); } function getSwapOrders( address payable _orderBookAddress, address _account, uint256[] memory _indices ) external view returns (uint256[] memory, address[] memory) { Vars memory vars = Vars(0, 0, _account, 5, 3); uint256[] memory uintProps = new uint256[](vars.uintLength * _indices.length); address[] memory addressProps = new address[](vars.addressLength * _indices.length); IOrderBook orderBook = IOrderBook(_orderBookAddress); while (vars.i < _indices.length) { vars.index = _indices[vars.i]; ( address path0, address path1, address path2, uint256 amountIn, uint256 minOut, uint256 triggerRatio, bool triggerAboveThreshold, bool shouldUnwrap, // uint256 executionFee ) = orderBook.getSwapOrder(vars.account, vars.index); uintProps[vars.i * vars.uintLength] = uint256(amountIn); uintProps[vars.i * vars.uintLength + 1] = uint256(minOut); uintProps[vars.i * vars.uintLength + 2] = uint256(triggerRatio); uintProps[vars.i * vars.uintLength + 3] = uint256(triggerAboveThreshold ? 1 : 0); uintProps[vars.i * vars.uintLength + 4] = uint256(shouldUnwrap ? 1 : 0); addressProps[vars.i * vars.addressLength] = (path0); addressProps[vars.i * vars.addressLength + 1] = (path1); addressProps[vars.i * vars.addressLength + 2] = (path2); vars.i++; } return (uintProps, addressProps); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "./interfaces/IRouter.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IOrderBook.sol"; import "../peripherals/interfaces/ITimelock.sol"; import "./BasePositionManager.sol"; contract PositionManager is BasePositionManager { address public orderBook; bool public inLegacyMode; bool public shouldValidateIncreaseOrder = true; mapping (address => bool) public isOrderKeeper; mapping (address => bool) public isPartner; mapping (address => bool) public isLiquidator; event SetOrderKeeper(address indexed account, bool isActive); event SetLiquidator(address indexed account, bool isActive); event SetPartner(address account, bool isActive); event SetInLegacyMode(bool inLegacyMode); event SetShouldValidateIncreaseOrder(bool shouldValidateIncreaseOrder); modifier onlyOrderKeeper() { require(isOrderKeeper[msg.sender], "PositionManager: forbidden"); _; } modifier onlyLiquidator() { require(isLiquidator[msg.sender], "PositionManager: forbidden"); _; } modifier onlyPartnersOrLegacyMode() { require(isPartner[msg.sender] || inLegacyMode, "PositionManager: forbidden"); _; } constructor( address _vault, address _router, address _shortsTracker, address _weth, uint256 _depositFee, address _orderBook ) public BasePositionManager(_vault, _router, _shortsTracker, _weth, _depositFee) { orderBook = _orderBook; } function setOrderKeeper(address _account, bool _isActive) external onlyAdmin { isOrderKeeper[_account] = _isActive; emit SetOrderKeeper(_account, _isActive); } function setLiquidator(address _account, bool _isActive) external onlyAdmin { isLiquidator[_account] = _isActive; emit SetLiquidator(_account, _isActive); } function setPartner(address _account, bool _isActive) external onlyAdmin { isPartner[_account] = _isActive; emit SetPartner(_account, _isActive); } function setInLegacyMode(bool _inLegacyMode) external onlyAdmin { inLegacyMode = _inLegacyMode; emit SetInLegacyMode(_inLegacyMode); } function setShouldValidateIncreaseOrder(bool _shouldValidateIncreaseOrder) external onlyAdmin { shouldValidateIncreaseOrder = _shouldValidateIncreaseOrder; emit SetShouldValidateIncreaseOrder(_shouldValidateIncreaseOrder); } function increasePosition( address[] memory _path, address _indexToken, uint256 _amountIn, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _price ) external nonReentrant onlyPartnersOrLegacyMode { require(_path.length == 1 || _path.length == 2, "PositionManager: invalid _path.length"); if (_amountIn > 0) { if (_path.length == 1) { IRouter(router).pluginTransfer(_path[0], msg.sender, address(this), _amountIn); } else { IRouter(router).pluginTransfer(_path[0], msg.sender, vault, _amountIn); _amountIn = _swap(_path, _minOut, address(this)); } uint256 afterFeeAmount = _collectFees(msg.sender, _path, _amountIn, _indexToken, _isLong, _sizeDelta); IERC20(_path[_path.length - 1]).safeTransfer(vault, afterFeeAmount); } _increasePosition(msg.sender, _path[_path.length - 1], _indexToken, _sizeDelta, _isLong, _price); } function increasePositionETH( address[] memory _path, address _indexToken, uint256 _minOut, uint256 _sizeDelta, bool _isLong, uint256 _price ) external payable nonReentrant onlyPartnersOrLegacyMode { require(_path.length == 1 || _path.length == 2, "PositionManager: invalid _path.length"); require(_path[0] == weth, "PositionManager: invalid _path"); if (msg.value > 0) { _transferInETH(); uint256 _amountIn = msg.value; if (_path.length > 1) { IERC20(weth).safeTransfer(vault, msg.value); _amountIn = _swap(_path, _minOut, address(this)); } uint256 afterFeeAmount = _collectFees(msg.sender, _path, _amountIn, _indexToken, _isLong, _sizeDelta); IERC20(_path[_path.length - 1]).safeTransfer(vault, afterFeeAmount); } _increasePosition(msg.sender, _path[_path.length - 1], _indexToken, _sizeDelta, _isLong, _price); } function decreasePosition( address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price ) external nonReentrant onlyPartnersOrLegacyMode { _decreasePosition(msg.sender, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, _receiver, _price); } function decreasePositionETH( address _collateralToken, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address payable _receiver, uint256 _price ) external nonReentrant onlyPartnersOrLegacyMode { require(_collateralToken == weth, "PositionManager: invalid _collateralToken"); uint256 amountOut = _decreasePosition(msg.sender, _collateralToken, _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); _transferOutETHWithGasLimitFallbackToWeth(amountOut, _receiver); } function decreasePositionAndSwap( address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address _receiver, uint256 _price, uint256 _minOut ) external nonReentrant onlyPartnersOrLegacyMode { require(_path.length == 2, "PositionManager: invalid _path.length"); uint256 amount = _decreasePosition(msg.sender, _path[0], _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); IERC20(_path[0]).safeTransfer(vault, amount); _swap(_path, _minOut, _receiver); } function decreasePositionAndSwapETH( address[] memory _path, address _indexToken, uint256 _collateralDelta, uint256 _sizeDelta, bool _isLong, address payable _receiver, uint256 _price, uint256 _minOut ) external nonReentrant onlyPartnersOrLegacyMode { require(_path.length == 2, "PositionManager: invalid _path.length"); require(_path[_path.length - 1] == weth, "PositionManager: invalid _path"); uint256 amount = _decreasePosition(msg.sender, _path[0], _indexToken, _collateralDelta, _sizeDelta, _isLong, address(this), _price); IERC20(_path[0]).safeTransfer(vault, amount); uint256 amountOut = _swap(_path, _minOut, address(this)); _transferOutETHWithGasLimitFallbackToWeth(amountOut, _receiver); } function liquidatePosition( address _account, address _collateralToken, address _indexToken, bool _isLong, address _feeReceiver ) external nonReentrant onlyLiquidator { address _vault = vault; address timelock = IVault(_vault).gov(); (uint256 size, , , , , , , ) = IVault(vault).getPosition(_account, _collateralToken, _indexToken, _isLong); uint256 markPrice = _isLong ? IVault(_vault).getMinPrice(_indexToken) : IVault(_vault).getMaxPrice(_indexToken); // should be called strictly before position is updated in Vault IShortsTracker(shortsTracker).updateGlobalShortData(_account, _collateralToken, _indexToken, _isLong, size, markPrice, false); ITimelock(timelock).enableLeverage(_vault); IVault(_vault).liquidatePosition(_account, _collateralToken, _indexToken, _isLong, _feeReceiver); ITimelock(timelock).disableLeverage(_vault); } function executeSwapOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) external onlyOrderKeeper { IOrderBook(orderBook).executeSwapOrder(_account, _orderIndex, _feeReceiver); } function executeIncreaseOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) external onlyOrderKeeper { _validateIncreaseOrder(_account, _orderIndex); address _vault = vault; address timelock = IVault(_vault).gov(); ( /*address purchaseToken*/, /*uint256 purchaseTokenAmount*/, address collateralToken, address indexToken, uint256 sizeDelta, bool isLong, /*uint256 triggerPrice*/, /*bool triggerAboveThreshold*/, /*uint256 executionFee*/ ) = IOrderBook(orderBook).getIncreaseOrder(_account, _orderIndex); uint256 markPrice = isLong ? IVault(_vault).getMaxPrice(indexToken) : IVault(_vault).getMinPrice(indexToken); // should be called strictly before position is updated in Vault IShortsTracker(shortsTracker).updateGlobalShortData(_account, collateralToken, indexToken, isLong, sizeDelta, markPrice, true); ITimelock(timelock).enableLeverage(_vault); IOrderBook(orderBook).executeIncreaseOrder(_account, _orderIndex, _feeReceiver); ITimelock(timelock).disableLeverage(_vault); _emitIncreasePositionReferral(_account, sizeDelta); } function executeDecreaseOrder(address _account, uint256 _orderIndex, address payable _feeReceiver) external onlyOrderKeeper { address _vault = vault; address timelock = IVault(_vault).gov(); ( address collateralToken, /*uint256 collateralDelta*/, address indexToken, uint256 sizeDelta, bool isLong, /*uint256 triggerPrice*/, /*bool triggerAboveThreshold*/, /*uint256 executionFee*/ ) = IOrderBook(orderBook).getDecreaseOrder(_account, _orderIndex); uint256 markPrice = isLong ? IVault(_vault).getMinPrice(indexToken) : IVault(_vault).getMaxPrice(indexToken); // should be called strictly before position is updated in Vault IShortsTracker(shortsTracker).updateGlobalShortData(_account, collateralToken, indexToken, isLong, sizeDelta, markPrice, false); ITimelock(timelock).enableLeverage(_vault); IOrderBook(orderBook).executeDecreaseOrder(_account, _orderIndex, _feeReceiver); ITimelock(timelock).disableLeverage(_vault); _emitDecreasePositionReferral(_account, sizeDelta); } function _validateIncreaseOrder(address _account, uint256 _orderIndex) internal view { ( address _purchaseToken, uint256 _purchaseTokenAmount, address _collateralToken, address _indexToken, uint256 _sizeDelta, bool _isLong, , // triggerPrice , // triggerAboveThreshold // executionFee ) = IOrderBook(orderBook).getIncreaseOrder(_account, _orderIndex); _validateMaxGlobalSize(_indexToken, _isLong, _sizeDelta); if (!shouldValidateIncreaseOrder) { return; } // shorts are okay if (!_isLong) { return; } // if the position size is not increasing, this is a collateral deposit require(_sizeDelta > 0, "PositionManager: long deposit"); IVault _vault = IVault(vault); (uint256 size, uint256 collateral, , , , , , ) = _vault.getPosition(_account, _collateralToken, _indexToken, _isLong); // if there is no existing position, do not charge a fee if (size == 0) { return; } uint256 nextSize = size.add(_sizeDelta); uint256 collateralDelta = _vault.tokenToUsdMin(_purchaseToken, _purchaseTokenAmount); uint256 nextCollateral = collateral.add(collateralDelta); uint256 prevLeverage = size.mul(BASIS_POINTS_DIVISOR).div(collateral); // allow for a maximum of a increasePositionBufferBps decrease since there might be some swap fees taken from the collateral uint256 nextLeverageWithBuffer = nextSize.mul(BASIS_POINTS_DIVISOR + increasePositionBufferBps).div(nextCollateral); require(nextLeverageWithBuffer >= prevLeverage, "PositionManager: long leverage decrease"); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../staking/interfaces/IVester.sol"; import "../staking/interfaces/IRewardTracker.sol"; contract RewardReader { using SafeMath for uint256; function getDepositBalances(address _account, address[] memory _depositTokens, address[] memory _rewardTrackers) public view returns (uint256[] memory) { uint256[] memory amounts = new uint256[](_rewardTrackers.length); for (uint256 i = 0; i < _rewardTrackers.length; i++) { IRewardTracker rewardTracker = IRewardTracker(_rewardTrackers[i]); amounts[i] = rewardTracker.depositBalances(_account, _depositTokens[i]); } return amounts; } function getStakingInfo(address _account, address[] memory _rewardTrackers) public view returns (uint256[] memory) { uint256 propsLength = 5; uint256[] memory amounts = new uint256[](_rewardTrackers.length * propsLength); for (uint256 i = 0; i < _rewardTrackers.length; i++) { IRewardTracker rewardTracker = IRewardTracker(_rewardTrackers[i]); amounts[i * propsLength] = rewardTracker.claimable(_account); amounts[i * propsLength + 1] = rewardTracker.tokensPerInterval(); amounts[i * propsLength + 2] = rewardTracker.averageStakedAmounts(_account); amounts[i * propsLength + 3] = rewardTracker.cumulativeRewards(_account); amounts[i * propsLength + 4] = IERC20(_rewardTrackers[i]).totalSupply(); } return amounts; } function getVestingInfoV2(address _account, address[] memory _vesters) public view returns (uint256[] memory) { uint256 propsLength = 12; uint256[] memory amounts = new uint256[](_vesters.length * propsLength); for (uint256 i = 0; i < _vesters.length; i++) { IVester vester = IVester(_vesters[i]); IRewardTracker rewardTracker = IRewardTracker(vester.rewardTracker()); amounts[i * propsLength] = vester.pairAmounts(_account); amounts[i * propsLength + 1] = vester.getVestedAmount(_account); amounts[i * propsLength + 2] = IERC20(_vesters[i]).balanceOf(_account); amounts[i * propsLength + 3] = vester.claimedAmounts(_account); amounts[i * propsLength + 4] = vester.claimable(_account); amounts[i * propsLength + 5] = vester.getMaxVestableAmount(_account); amounts[i * propsLength + 6] = vester.getCombinedAverageStakedAmount(_account); amounts[i * propsLength + 7] = rewardTracker.cumulativeRewards(_account); amounts[i * propsLength + 8] = vester.transferredCumulativeRewards(_account); amounts[i * propsLength + 9] = vester.bonusRewards(_account); amounts[i * propsLength + 10] = rewardTracker.averageStakedAmounts(_account); amounts[i * propsLength + 11] = vester.transferredAverageStakedAmounts(_account); } return amounts; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../staking/interfaces/IVester.sol"; import "../staking/interfaces/IRewardTracker.sol"; contract EsGmxBatchSender { using SafeMath for uint256; address public admin; address public esGmx; constructor(address _esGmx) public { admin = msg.sender; esGmx = _esGmx; } modifier onlyAdmin() { require(msg.sender == admin, "EsGmxBatchSender: forbidden"); _; } function send( IVester _vester, uint256 _minRatio, address[] memory _accounts, uint256[] memory _amounts ) external onlyAdmin { IRewardTracker rewardTracker = IRewardTracker(_vester.rewardTracker()); for (uint256 i = 0; i < _accounts.length; i++) { IERC20(esGmx).transferFrom(msg.sender, _accounts[i], _amounts[i]); uint256 nextTransferredCumulativeReward = _vester.transferredCumulativeRewards(_accounts[i]).add(_amounts[i]); _vester.setTransferredCumulativeRewards(_accounts[i], nextTransferredCumulativeReward); uint256 cumulativeReward = rewardTracker.cumulativeRewards(_accounts[i]); uint256 totalCumulativeReward = cumulativeReward.add(nextTransferredCumulativeReward); uint256 combinedAverageStakedAmount = _vester.getCombinedAverageStakedAmount(_accounts[i]); if (combinedAverageStakedAmount > totalCumulativeReward.mul(_minRatio)) { continue; } uint256 nextTransferredAverageStakedAmount = _minRatio.mul(totalCumulativeReward); nextTransferredAverageStakedAmount = nextTransferredAverageStakedAmount.sub( rewardTracker.averageStakedAmounts(_accounts[i]).mul(cumulativeReward).div(totalCumulativeReward) ); nextTransferredAverageStakedAmount = nextTransferredAverageStakedAmount.mul(totalCumulativeReward).div(nextTransferredCumulativeReward); _vester.setTransferredAverageStakedAmounts(_accounts[i], nextTransferredAverageStakedAmount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../core/interfaces/IGlpManager.sol"; contract GlpBalance { using SafeMath for uint256; IGlpManager public glpManager; address public stakedGlpTracker; mapping (address => mapping (address => uint256)) public allowances; event Approval(address indexed owner, address indexed spender, uint256 value); constructor( IGlpManager _glpManager, address _stakedGlpTracker ) public { glpManager = _glpManager; stakedGlpTracker = _stakedGlpTracker; } function allowance(address _owner, address _spender) external view returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transfer(address _recipient, uint256 _amount) external returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool) { uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "GlpBalance: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "GlpBalance: approve from the zero address"); require(_spender != address(0), "GlpBalance: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "GlpBalance: transfer from the zero address"); require(_recipient != address(0), "GlpBalance: transfer to the zero address"); require( glpManager.lastAddedAt(_sender).add(glpManager.cooldownDuration()) <= block.timestamp, "GlpBalance: cooldown duration not yet passed" ); IERC20(stakedGlpTracker).transferFrom(_sender, _recipient, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "../interfaces/IPositionRouterCallbackReceiver.sol"; contract PositionRouterCallbackReceiverTest is IPositionRouterCallbackReceiver { event CallbackCalled( bytes32 positionKey, bool isExecuted, bool isIncrease ); function gmxPositionCallback(bytes32 positionKey, bool isExecuted, bool isIncrease) override external { emit CallbackCalled(positionKey, isExecuted, isIncrease); } }
// SPDX-License-Identifier: MIT import "../libraries/math/SafeMath.sol"; import "./interfaces/ISecondaryPriceFeed.sol"; import "./interfaces/IFastPriceFeed.sol"; import "./interfaces/IFastPriceEvents.sol"; import "../core/interfaces/IVaultPriceFeed.sol"; import "../core/interfaces/IPositionRouter.sol"; import "../access/Governable.sol"; pragma solidity 0.6.12; contract FastPriceFeed is ISecondaryPriceFeed, IFastPriceFeed, Governable { using SafeMath for uint256; // fit data in a uint256 slot to save gas costs struct PriceDataItem { uint160 refPrice; // Chainlink price uint32 refTime; // last updated at time uint32 cumulativeRefDelta; // cumulative Chainlink price delta uint32 cumulativeFastDelta; // cumulative fast price delta } uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant CUMULATIVE_DELTA_PRECISION = 10 * 1000 * 1000; uint256 public constant MAX_REF_PRICE = type(uint160).max; uint256 public constant MAX_CUMULATIVE_REF_DELTA = type(uint32).max; uint256 public constant MAX_CUMULATIVE_FAST_DELTA = type(uint32).max; // uint256(~0) is 256 bits of 1s // shift the 1s by (256 - 32) to get (256 - 32) 0s followed by 32 1s uint256 constant public BITMASK_32 = uint256(~0) >> (256 - 32); uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant MAX_PRICE_DURATION = 30 minutes; bool public isInitialized; bool public isSpreadEnabled = false; address public vaultPriceFeed; address public fastPriceEvents; address public tokenManager; uint256 public override lastUpdatedAt; uint256 public override lastUpdatedBlock; uint256 public priceDuration; uint256 public maxPriceUpdateDelay; uint256 public spreadBasisPointsIfInactive; uint256 public spreadBasisPointsIfChainError; uint256 public minBlockInterval; uint256 public maxTimeDeviation; uint256 public priceDataInterval; // allowed deviation from primary price uint256 public maxDeviationBasisPoints; uint256 public minAuthorizations; uint256 public disableFastPriceVoteCount = 0; mapping (address => bool) public isUpdater; mapping (address => uint256) public prices; mapping (address => PriceDataItem) public priceData; mapping (address => uint256) public maxCumulativeDeltaDiffs; mapping (address => bool) public isSigner; mapping (address => bool) public disableFastPriceVotes; // array of tokens used in setCompactedPrices, saves L1 calldata gas costs address[] public tokens; // array of tokenPrecisions used in setCompactedPrices, saves L1 calldata gas costs // if the token price will be sent with 3 decimals, then tokenPrecision for that token // should be 10 ** 3 uint256[] public tokenPrecisions; event DisableFastPrice(address signer); event EnableFastPrice(address signer); event PriceData(address token, uint256 refPrice, uint256 fastPrice, uint256 cumulativeRefDelta, uint256 cumulativeFastDelta); event MaxCumulativeDeltaDiffExceeded(address token, uint256 refPrice, uint256 fastPrice, uint256 cumulativeRefDelta, uint256 cumulativeFastDelta); modifier onlySigner() { require(isSigner[msg.sender], "FastPriceFeed: forbidden"); _; } modifier onlyUpdater() { require(isUpdater[msg.sender], "FastPriceFeed: forbidden"); _; } modifier onlyTokenManager() { require(msg.sender == tokenManager, "FastPriceFeed: forbidden"); _; } constructor( uint256 _priceDuration, uint256 _maxPriceUpdateDelay, uint256 _minBlockInterval, uint256 _maxDeviationBasisPoints, address _fastPriceEvents, address _tokenManager ) public { require(_priceDuration <= MAX_PRICE_DURATION, "FastPriceFeed: invalid _priceDuration"); priceDuration = _priceDuration; maxPriceUpdateDelay = _maxPriceUpdateDelay; minBlockInterval = _minBlockInterval; maxDeviationBasisPoints = _maxDeviationBasisPoints; fastPriceEvents = _fastPriceEvents; tokenManager = _tokenManager; } function initialize(uint256 _minAuthorizations, address[] memory _signers, address[] memory _updaters) public onlyGov { require(!isInitialized, "FastPriceFeed: already initialized"); isInitialized = true; minAuthorizations = _minAuthorizations; for (uint256 i = 0; i < _signers.length; i++) { address signer = _signers[i]; isSigner[signer] = true; } for (uint256 i = 0; i < _updaters.length; i++) { address updater = _updaters[i]; isUpdater[updater] = true; } } function setSigner(address _account, bool _isActive) external override onlyGov { isSigner[_account] = _isActive; } function setUpdater(address _account, bool _isActive) external override onlyGov { isUpdater[_account] = _isActive; } function setFastPriceEvents(address _fastPriceEvents) external onlyGov { fastPriceEvents = _fastPriceEvents; } function setVaultPriceFeed(address _vaultPriceFeed) external override onlyGov { vaultPriceFeed = _vaultPriceFeed; } function setMaxTimeDeviation(uint256 _maxTimeDeviation) external onlyGov { maxTimeDeviation = _maxTimeDeviation; } function setPriceDuration(uint256 _priceDuration) external override onlyGov { require(_priceDuration <= MAX_PRICE_DURATION, "FastPriceFeed: invalid _priceDuration"); priceDuration = _priceDuration; } function setMaxPriceUpdateDelay(uint256 _maxPriceUpdateDelay) external override onlyGov { maxPriceUpdateDelay = _maxPriceUpdateDelay; } function setSpreadBasisPointsIfInactive(uint256 _spreadBasisPointsIfInactive) external override onlyGov { spreadBasisPointsIfInactive = _spreadBasisPointsIfInactive; } function setSpreadBasisPointsIfChainError(uint256 _spreadBasisPointsIfChainError) external override onlyGov { spreadBasisPointsIfChainError = _spreadBasisPointsIfChainError; } function setMinBlockInterval(uint256 _minBlockInterval) external override onlyGov { minBlockInterval = _minBlockInterval; } function setIsSpreadEnabled(bool _isSpreadEnabled) external override onlyGov { isSpreadEnabled = _isSpreadEnabled; } function setLastUpdatedAt(uint256 _lastUpdatedAt) external onlyGov { lastUpdatedAt = _lastUpdatedAt; } function setTokenManager(address _tokenManager) external onlyTokenManager { tokenManager = _tokenManager; } function setMaxDeviationBasisPoints(uint256 _maxDeviationBasisPoints) external override onlyTokenManager { maxDeviationBasisPoints = _maxDeviationBasisPoints; } function setMaxCumulativeDeltaDiffs(address[] memory _tokens, uint256[] memory _maxCumulativeDeltaDiffs) external override onlyTokenManager { for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; maxCumulativeDeltaDiffs[token] = _maxCumulativeDeltaDiffs[i]; } } function setPriceDataInterval(uint256 _priceDataInterval) external override onlyTokenManager { priceDataInterval = _priceDataInterval; } function setMinAuthorizations(uint256 _minAuthorizations) external onlyTokenManager { minAuthorizations = _minAuthorizations; } function setTokens(address[] memory _tokens, uint256[] memory _tokenPrecisions) external onlyGov { require(_tokens.length == _tokenPrecisions.length, "FastPriceFeed: invalid lengths"); tokens = _tokens; tokenPrecisions = _tokenPrecisions; } function setPrices(address[] memory _tokens, uint256[] memory _prices, uint256 _timestamp) external onlyUpdater { bool shouldUpdate = _setLastUpdatedValues(_timestamp); if (shouldUpdate) { address _fastPriceEvents = fastPriceEvents; address _vaultPriceFeed = vaultPriceFeed; for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; _setPrice(token, _prices[i], _vaultPriceFeed, _fastPriceEvents); } } } function setCompactedPrices(uint256[] memory _priceBitArray, uint256 _timestamp) external onlyUpdater { bool shouldUpdate = _setLastUpdatedValues(_timestamp); if (shouldUpdate) { address _fastPriceEvents = fastPriceEvents; address _vaultPriceFeed = vaultPriceFeed; for (uint256 i = 0; i < _priceBitArray.length; i++) { uint256 priceBits = _priceBitArray[i]; for (uint256 j = 0; j < 8; j++) { uint256 index = i * 8 + j; if (index >= tokens.length) { return; } uint256 startBit = 32 * j; uint256 price = (priceBits >> startBit) & BITMASK_32; address token = tokens[i * 8 + j]; uint256 tokenPrecision = tokenPrecisions[i * 8 + j]; uint256 adjustedPrice = price.mul(PRICE_PRECISION).div(tokenPrecision); _setPrice(token, adjustedPrice, _vaultPriceFeed, _fastPriceEvents); } } } } function setPricesWithBits(uint256 _priceBits, uint256 _timestamp) external onlyUpdater { _setPricesWithBits(_priceBits, _timestamp); } function setPricesWithBitsAndExecute( address _positionRouter, uint256 _priceBits, uint256 _timestamp, uint256 _endIndexForIncreasePositions, uint256 _endIndexForDecreasePositions, uint256 _maxIncreasePositions, uint256 _maxDecreasePositions ) external onlyUpdater { _setPricesWithBits(_priceBits, _timestamp); IPositionRouter positionRouter = IPositionRouter(_positionRouter); uint256 maxEndIndexForIncrease = positionRouter.increasePositionRequestKeysStart().add(_maxIncreasePositions); uint256 maxEndIndexForDecrease = positionRouter.decreasePositionRequestKeysStart().add(_maxDecreasePositions); if (_endIndexForIncreasePositions > maxEndIndexForIncrease) { _endIndexForIncreasePositions = maxEndIndexForIncrease; } if (_endIndexForDecreasePositions > maxEndIndexForDecrease) { _endIndexForDecreasePositions = maxEndIndexForDecrease; } positionRouter.executeIncreasePositions(_endIndexForIncreasePositions, payable(msg.sender)); positionRouter.executeDecreasePositions(_endIndexForDecreasePositions, payable(msg.sender)); } function disableFastPrice() external onlySigner { require(!disableFastPriceVotes[msg.sender], "FastPriceFeed: already voted"); disableFastPriceVotes[msg.sender] = true; disableFastPriceVoteCount = disableFastPriceVoteCount.add(1); emit DisableFastPrice(msg.sender); } function enableFastPrice() external onlySigner { require(disableFastPriceVotes[msg.sender], "FastPriceFeed: already enabled"); disableFastPriceVotes[msg.sender] = false; disableFastPriceVoteCount = disableFastPriceVoteCount.sub(1); emit EnableFastPrice(msg.sender); } // under regular operation, the fastPrice (prices[token]) is returned and there is no spread returned from this function, // though VaultPriceFeed might apply its own spread // // if the fastPrice has not been updated within priceDuration then it is ignored and only _refPrice with a spread is used (spread: spreadBasisPointsIfInactive) // in case the fastPrice has not been updated for maxPriceUpdateDelay then the _refPrice with a larger spread is used (spread: spreadBasisPointsIfChainError) // // there will be a spread from the _refPrice to the fastPrice in the following cases: // - in case isSpreadEnabled is set to true // - in case the maxDeviationBasisPoints between _refPrice and fastPrice is exceeded // - in case watchers flag an issue // - in case the cumulativeFastDelta exceeds the cumulativeRefDelta by the maxCumulativeDeltaDiff function getPrice(address _token, uint256 _refPrice, bool _maximise) external override view returns (uint256) { if (block.timestamp > lastUpdatedAt.add(maxPriceUpdateDelay)) { if (_maximise) { return _refPrice.mul(BASIS_POINTS_DIVISOR.add(spreadBasisPointsIfChainError)).div(BASIS_POINTS_DIVISOR); } return _refPrice.mul(BASIS_POINTS_DIVISOR.sub(spreadBasisPointsIfChainError)).div(BASIS_POINTS_DIVISOR); } if (block.timestamp > lastUpdatedAt.add(priceDuration)) { if (_maximise) { return _refPrice.mul(BASIS_POINTS_DIVISOR.add(spreadBasisPointsIfInactive)).div(BASIS_POINTS_DIVISOR); } return _refPrice.mul(BASIS_POINTS_DIVISOR.sub(spreadBasisPointsIfInactive)).div(BASIS_POINTS_DIVISOR); } uint256 fastPrice = prices[_token]; if (fastPrice == 0) { return _refPrice; } uint256 diffBasisPoints = _refPrice > fastPrice ? _refPrice.sub(fastPrice) : fastPrice.sub(_refPrice); diffBasisPoints = diffBasisPoints.mul(BASIS_POINTS_DIVISOR).div(_refPrice); // create a spread between the _refPrice and the fastPrice if the maxDeviationBasisPoints is exceeded // or if watchers have flagged an issue with the fast price bool hasSpread = !favorFastPrice(_token) || diffBasisPoints > maxDeviationBasisPoints; if (hasSpread) { // return the higher of the two prices if (_maximise) { return _refPrice > fastPrice ? _refPrice : fastPrice; } // return the lower of the two prices return _refPrice < fastPrice ? _refPrice : fastPrice; } return fastPrice; } function favorFastPrice(address _token) public view returns (bool) { if (isSpreadEnabled) { return false; } if (disableFastPriceVoteCount >= minAuthorizations) { // force a spread if watchers have flagged an issue with the fast price return false; } (/* uint256 prevRefPrice */, /* uint256 refTime */, uint256 cumulativeRefDelta, uint256 cumulativeFastDelta) = getPriceData(_token); if (cumulativeFastDelta > cumulativeRefDelta && cumulativeFastDelta.sub(cumulativeRefDelta) > maxCumulativeDeltaDiffs[_token]) { // force a spread if the cumulative delta for the fast price feed exceeds the cumulative delta // for the Chainlink price feed by the maxCumulativeDeltaDiff allowed return false; } return true; } function getPriceData(address _token) public view returns (uint256, uint256, uint256, uint256) { PriceDataItem memory data = priceData[_token]; return (uint256(data.refPrice), uint256(data.refTime), uint256(data.cumulativeRefDelta), uint256(data.cumulativeFastDelta)); } function _setPricesWithBits(uint256 _priceBits, uint256 _timestamp) private { bool shouldUpdate = _setLastUpdatedValues(_timestamp); if (shouldUpdate) { address _fastPriceEvents = fastPriceEvents; address _vaultPriceFeed = vaultPriceFeed; for (uint256 j = 0; j < 8; j++) { uint256 index = j; if (index >= tokens.length) { return; } uint256 startBit = 32 * j; uint256 price = (_priceBits >> startBit) & BITMASK_32; address token = tokens[j]; uint256 tokenPrecision = tokenPrecisions[j]; uint256 adjustedPrice = price.mul(PRICE_PRECISION).div(tokenPrecision); _setPrice(token, adjustedPrice, _vaultPriceFeed, _fastPriceEvents); } } } function _setPrice(address _token, uint256 _price, address _vaultPriceFeed, address _fastPriceEvents) private { if (_vaultPriceFeed != address(0)) { uint256 refPrice = IVaultPriceFeed(_vaultPriceFeed).getLatestPrimaryPrice(_token); uint256 fastPrice = prices[_token]; (uint256 prevRefPrice, uint256 refTime, uint256 cumulativeRefDelta, uint256 cumulativeFastDelta) = getPriceData(_token); if (prevRefPrice > 0) { uint256 refDeltaAmount = refPrice > prevRefPrice ? refPrice.sub(prevRefPrice) : prevRefPrice.sub(refPrice); uint256 fastDeltaAmount = fastPrice > _price ? fastPrice.sub(_price) : _price.sub(fastPrice); // reset cumulative delta values if it is a new time window if (refTime.div(priceDataInterval) != block.timestamp.div(priceDataInterval)) { cumulativeRefDelta = 0; cumulativeFastDelta = 0; } cumulativeRefDelta = cumulativeRefDelta.add(refDeltaAmount.mul(CUMULATIVE_DELTA_PRECISION).div(prevRefPrice)); cumulativeFastDelta = cumulativeFastDelta.add(fastDeltaAmount.mul(CUMULATIVE_DELTA_PRECISION).div(fastPrice)); } if (cumulativeFastDelta > cumulativeRefDelta && cumulativeFastDelta.sub(cumulativeRefDelta) > maxCumulativeDeltaDiffs[_token]) { emit MaxCumulativeDeltaDiffExceeded(_token, refPrice, fastPrice, cumulativeRefDelta, cumulativeFastDelta); } _setPriceData(_token, refPrice, cumulativeRefDelta, cumulativeFastDelta); emit PriceData(_token, refPrice, fastPrice, cumulativeRefDelta, cumulativeFastDelta); } prices[_token] = _price; _emitPriceEvent(_fastPriceEvents, _token, _price); } function _setPriceData(address _token, uint256 _refPrice, uint256 _cumulativeRefDelta, uint256 _cumulativeFastDelta) private { require(_refPrice < MAX_REF_PRICE, "FastPriceFeed: invalid refPrice"); // skip validation of block.timestamp, it should only be out of range after the year 2100 require(_cumulativeRefDelta < MAX_CUMULATIVE_REF_DELTA, "FastPriceFeed: invalid cumulativeRefDelta"); require(_cumulativeFastDelta < MAX_CUMULATIVE_FAST_DELTA, "FastPriceFeed: invalid cumulativeFastDelta"); priceData[_token] = PriceDataItem( uint160(_refPrice), uint32(block.timestamp), uint32(_cumulativeRefDelta), uint32(_cumulativeFastDelta) ); } function _emitPriceEvent(address _fastPriceEvents, address _token, uint256 _price) private { if (_fastPriceEvents == address(0)) { return; } IFastPriceEvents(_fastPriceEvents).emitPriceEvent(_token, _price); } function _setLastUpdatedValues(uint256 _timestamp) private returns (bool) { if (minBlockInterval > 0) { require(block.number.sub(lastUpdatedBlock) >= minBlockInterval, "FastPriceFeed: minBlockInterval not yet passed"); } uint256 _maxTimeDeviation = maxTimeDeviation; require(_timestamp > block.timestamp.sub(_maxTimeDeviation), "FastPriceFeed: _timestamp below allowed range"); require(_timestamp < block.timestamp.add(_maxTimeDeviation), "FastPriceFeed: _timestamp exceeds allowed range"); // do not update prices if _timestamp is before the current lastUpdatedAt value if (_timestamp < lastUpdatedAt) { return false; } lastUpdatedAt = _timestamp; lastUpdatedBlock = block.number; return true; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface ISecondaryPriceFeed { function getPrice(address _token, uint256 _referencePrice, bool _maximise) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IFastPriceEvents { function emitPriceEvent(address _token, uint256 _price) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../core/interfaces/IVault.sol"; import "../core/interfaces/IVaultPriceFeed.sol"; import "../tokens/interfaces/IYieldTracker.sol"; import "../tokens/interfaces/IYieldToken.sol"; import "../amm/interfaces/IPancakeFactory.sol"; import "../staking/interfaces/IVester.sol"; import "../access/Governable.sol"; contract Reader is Governable { using SafeMath for uint256; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant POSITION_PROPS_LENGTH = 9; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant USDG_DECIMALS = 18; bool public hasMaxGlobalShortSizes; function setConfig(bool _hasMaxGlobalShortSizes) public onlyGov { hasMaxGlobalShortSizes = _hasMaxGlobalShortSizes; } function getMaxAmountIn(IVault _vault, address _tokenIn, address _tokenOut) public view returns (uint256) { uint256 priceIn = _vault.getMinPrice(_tokenIn); uint256 priceOut = _vault.getMaxPrice(_tokenOut); uint256 tokenInDecimals = _vault.tokenDecimals(_tokenIn); uint256 tokenOutDecimals = _vault.tokenDecimals(_tokenOut); uint256 amountIn; { uint256 poolAmount = _vault.poolAmounts(_tokenOut); uint256 reservedAmount = _vault.reservedAmounts(_tokenOut); uint256 bufferAmount = _vault.bufferAmounts(_tokenOut); uint256 subAmount = reservedAmount > bufferAmount ? reservedAmount : bufferAmount; if (subAmount >= poolAmount) { return 0; } uint256 availableAmount = poolAmount.sub(subAmount); amountIn = availableAmount.mul(priceOut).div(priceIn).mul(10 ** tokenInDecimals).div(10 ** tokenOutDecimals); } uint256 maxUsdgAmount = _vault.maxUsdgAmounts(_tokenIn); if (maxUsdgAmount != 0) { if (maxUsdgAmount < _vault.usdgAmounts(_tokenIn)) { return 0; } uint256 maxAmountIn = maxUsdgAmount.sub(_vault.usdgAmounts(_tokenIn)); maxAmountIn = maxAmountIn.mul(10 ** tokenInDecimals).div(10 ** USDG_DECIMALS); maxAmountIn = maxAmountIn.mul(PRICE_PRECISION).div(priceIn); if (amountIn > maxAmountIn) { return maxAmountIn; } } return amountIn; } function getAmountOut(IVault _vault, address _tokenIn, address _tokenOut, uint256 _amountIn) public view returns (uint256, uint256) { uint256 priceIn = _vault.getMinPrice(_tokenIn); uint256 tokenInDecimals = _vault.tokenDecimals(_tokenIn); uint256 tokenOutDecimals = _vault.tokenDecimals(_tokenOut); uint256 feeBasisPoints; { uint256 usdgAmount = _amountIn.mul(priceIn).div(PRICE_PRECISION); usdgAmount = usdgAmount.mul(10 ** USDG_DECIMALS).div(10 ** tokenInDecimals); bool isStableSwap = _vault.stableTokens(_tokenIn) && _vault.stableTokens(_tokenOut); uint256 baseBps = isStableSwap ? _vault.stableSwapFeeBasisPoints() : _vault.swapFeeBasisPoints(); uint256 taxBps = isStableSwap ? _vault.stableTaxBasisPoints() : _vault.taxBasisPoints(); uint256 feesBasisPoints0 = _vault.getFeeBasisPoints(_tokenIn, usdgAmount, baseBps, taxBps, true); uint256 feesBasisPoints1 = _vault.getFeeBasisPoints(_tokenOut, usdgAmount, baseBps, taxBps, false); // use the higher of the two fee basis points feeBasisPoints = feesBasisPoints0 > feesBasisPoints1 ? feesBasisPoints0 : feesBasisPoints1; } uint256 priceOut = _vault.getMaxPrice(_tokenOut); uint256 amountOut = _amountIn.mul(priceIn).div(priceOut); amountOut = amountOut.mul(10 ** tokenOutDecimals).div(10 ** tokenInDecimals); uint256 amountOutAfterFees = amountOut.mul(BASIS_POINTS_DIVISOR.sub(feeBasisPoints)).div(BASIS_POINTS_DIVISOR); uint256 feeAmount = amountOut.sub(amountOutAfterFees); return (amountOutAfterFees, feeAmount); } function getFeeBasisPoints(IVault _vault, address _tokenIn, address _tokenOut, uint256 _amountIn) public view returns (uint256, uint256, uint256) { uint256 priceIn = _vault.getMinPrice(_tokenIn); uint256 tokenInDecimals = _vault.tokenDecimals(_tokenIn); uint256 usdgAmount = _amountIn.mul(priceIn).div(PRICE_PRECISION); usdgAmount = usdgAmount.mul(10 ** USDG_DECIMALS).div(10 ** tokenInDecimals); bool isStableSwap = _vault.stableTokens(_tokenIn) && _vault.stableTokens(_tokenOut); uint256 baseBps = isStableSwap ? _vault.stableSwapFeeBasisPoints() : _vault.swapFeeBasisPoints(); uint256 taxBps = isStableSwap ? _vault.stableTaxBasisPoints() : _vault.taxBasisPoints(); uint256 feesBasisPoints0 = _vault.getFeeBasisPoints(_tokenIn, usdgAmount, baseBps, taxBps, true); uint256 feesBasisPoints1 = _vault.getFeeBasisPoints(_tokenOut, usdgAmount, baseBps, taxBps, false); // use the higher of the two fee basis points uint256 feeBasisPoints = feesBasisPoints0 > feesBasisPoints1 ? feesBasisPoints0 : feesBasisPoints1; return (feeBasisPoints, feesBasisPoints0, feesBasisPoints1); } function getFees(address _vault, address[] memory _tokens) public view returns (uint256[] memory) { uint256[] memory amounts = new uint256[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { amounts[i] = IVault(_vault).feeReserves(_tokens[i]); } return amounts; } function getTotalStaked(address[] memory _yieldTokens) public view returns (uint256[] memory) { uint256[] memory amounts = new uint256[](_yieldTokens.length); for (uint256 i = 0; i < _yieldTokens.length; i++) { IYieldToken yieldToken = IYieldToken(_yieldTokens[i]); amounts[i] = yieldToken.totalStaked(); } return amounts; } function getStakingInfo(address _account, address[] memory _yieldTrackers) public view returns (uint256[] memory) { uint256 propsLength = 2; uint256[] memory amounts = new uint256[](_yieldTrackers.length * propsLength); for (uint256 i = 0; i < _yieldTrackers.length; i++) { IYieldTracker yieldTracker = IYieldTracker(_yieldTrackers[i]); amounts[i * propsLength] = yieldTracker.claimable(_account); amounts[i * propsLength + 1] = yieldTracker.getTokensPerInterval(); } return amounts; } function getVestingInfo(address _account, address[] memory _vesters) public view returns (uint256[] memory) { uint256 propsLength = 7; uint256[] memory amounts = new uint256[](_vesters.length * propsLength); for (uint256 i = 0; i < _vesters.length; i++) { IVester vester = IVester(_vesters[i]); amounts[i * propsLength] = vester.pairAmounts(_account); amounts[i * propsLength + 1] = vester.getVestedAmount(_account); amounts[i * propsLength + 2] = IERC20(_vesters[i]).balanceOf(_account); amounts[i * propsLength + 3] = vester.claimedAmounts(_account); amounts[i * propsLength + 4] = vester.claimable(_account); amounts[i * propsLength + 5] = vester.getMaxVestableAmount(_account); amounts[i * propsLength + 6] = vester.getCombinedAverageStakedAmount(_account); } return amounts; } function getPairInfo(address _factory, address[] memory _tokens) public view returns (uint256[] memory) { uint256 inputLength = 2; uint256 propsLength = 2; uint256[] memory amounts = new uint256[](_tokens.length / inputLength * propsLength); for (uint256 i = 0; i < _tokens.length / inputLength; i++) { address token0 = _tokens[i * inputLength]; address token1 = _tokens[i * inputLength + 1]; address pair = IPancakeFactory(_factory).getPair(token0, token1); amounts[i * propsLength] = IERC20(token0).balanceOf(pair); amounts[i * propsLength + 1] = IERC20(token1).balanceOf(pair); } return amounts; } function getFundingRates(address _vault, address _weth, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 2; uint256[] memory fundingRates = new uint256[](_tokens.length * propsLength); IVault vault = IVault(_vault); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } uint256 fundingRateFactor = vault.stableTokens(token) ? vault.stableFundingRateFactor() : vault.fundingRateFactor(); uint256 reservedAmount = vault.reservedAmounts(token); uint256 poolAmount = vault.poolAmounts(token); if (poolAmount > 0) { fundingRates[i * propsLength] = fundingRateFactor.mul(reservedAmount).div(poolAmount); } if (vault.cumulativeFundingRates(token) > 0) { uint256 nextRate = vault.getNextFundingRate(token); uint256 baseRate = vault.cumulativeFundingRates(token); fundingRates[i * propsLength + 1] = baseRate.add(nextRate); } } return fundingRates; } function getTokenSupply(IERC20 _token, address[] memory _excludedAccounts) public view returns (uint256) { uint256 supply = _token.totalSupply(); for (uint256 i = 0; i < _excludedAccounts.length; i++) { address account = _excludedAccounts[i]; uint256 balance = _token.balanceOf(account); supply = supply.sub(balance); } return supply; } function getTotalBalance(IERC20 _token, address[] memory _accounts) public view returns (uint256) { uint256 totalBalance = 0; for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; uint256 balance = _token.balanceOf(account); totalBalance = totalBalance.add(balance); } return totalBalance; } function getTokenBalances(address _account, address[] memory _tokens) public view returns (uint256[] memory) { uint256[] memory balances = new uint256[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { balances[i] = _account.balance; continue; } balances[i] = IERC20(token).balanceOf(_account); } return balances; } function getTokenBalancesWithSupplies(address _account, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 2; uint256[] memory balances = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { balances[i * propsLength] = _account.balance; balances[i * propsLength + 1] = 0; continue; } balances[i * propsLength] = IERC20(token).balanceOf(_account); balances[i * propsLength + 1] = IERC20(token).totalSupply(); } return balances; } function getPrices(IVaultPriceFeed _priceFeed, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 6; uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; amounts[i * propsLength] = _priceFeed.getPrice(token, true, true, false); amounts[i * propsLength + 1] = _priceFeed.getPrice(token, false, true, false); amounts[i * propsLength + 2] = _priceFeed.getPrimaryPrice(token, true); amounts[i * propsLength + 3] = _priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 4] = _priceFeed.isAdjustmentAdditive(token) ? 1 : 0; amounts[i * propsLength + 5] = _priceFeed.adjustmentBasisPoints(token); } return amounts; } function getVaultTokenInfo(address _vault, address _weth, uint256 _usdgAmount, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 10; IVault vault = IVault(_vault); IVaultPriceFeed priceFeed = IVaultPriceFeed(vault.priceFeed()); uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } amounts[i * propsLength] = vault.poolAmounts(token); amounts[i * propsLength + 1] = vault.reservedAmounts(token); amounts[i * propsLength + 2] = vault.usdgAmounts(token); amounts[i * propsLength + 3] = vault.getRedemptionAmount(token, _usdgAmount); amounts[i * propsLength + 4] = vault.tokenWeights(token); amounts[i * propsLength + 5] = vault.getMinPrice(token); amounts[i * propsLength + 6] = vault.getMaxPrice(token); amounts[i * propsLength + 7] = vault.guaranteedUsd(token); amounts[i * propsLength + 8] = priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 9] = priceFeed.getPrimaryPrice(token, true); } return amounts; } function getFullVaultTokenInfo(address _vault, address _weth, uint256 _usdgAmount, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 12; IVault vault = IVault(_vault); IVaultPriceFeed priceFeed = IVaultPriceFeed(vault.priceFeed()); uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } amounts[i * propsLength] = vault.poolAmounts(token); amounts[i * propsLength + 1] = vault.reservedAmounts(token); amounts[i * propsLength + 2] = vault.usdgAmounts(token); amounts[i * propsLength + 3] = vault.getRedemptionAmount(token, _usdgAmount); amounts[i * propsLength + 4] = vault.tokenWeights(token); amounts[i * propsLength + 5] = vault.bufferAmounts(token); amounts[i * propsLength + 6] = vault.maxUsdgAmounts(token); amounts[i * propsLength + 7] = vault.getMinPrice(token); amounts[i * propsLength + 8] = vault.getMaxPrice(token); amounts[i * propsLength + 9] = vault.guaranteedUsd(token); amounts[i * propsLength + 10] = priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 11] = priceFeed.getPrimaryPrice(token, true); } return amounts; } function getVaultTokenInfoV2(address _vault, address _weth, uint256 _usdgAmount, address[] memory _tokens) public view returns (uint256[] memory) { uint256 propsLength = 14; IVault vault = IVault(_vault); IVaultPriceFeed priceFeed = IVaultPriceFeed(vault.priceFeed()); uint256[] memory amounts = new uint256[](_tokens.length * propsLength); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; if (token == address(0)) { token = _weth; } uint256 maxGlobalShortSize = hasMaxGlobalShortSizes ? vault.maxGlobalShortSizes(token) : 0; amounts[i * propsLength] = vault.poolAmounts(token); amounts[i * propsLength + 1] = vault.reservedAmounts(token); amounts[i * propsLength + 2] = vault.usdgAmounts(token); amounts[i * propsLength + 3] = vault.getRedemptionAmount(token, _usdgAmount); amounts[i * propsLength + 4] = vault.tokenWeights(token); amounts[i * propsLength + 5] = vault.bufferAmounts(token); amounts[i * propsLength + 6] = vault.maxUsdgAmounts(token); amounts[i * propsLength + 7] = vault.globalShortSizes(token); amounts[i * propsLength + 8] = maxGlobalShortSize; amounts[i * propsLength + 9] = vault.getMinPrice(token); amounts[i * propsLength + 10] = vault.getMaxPrice(token); amounts[i * propsLength + 11] = vault.guaranteedUsd(token); amounts[i * propsLength + 12] = priceFeed.getPrimaryPrice(token, false); amounts[i * propsLength + 13] = priceFeed.getPrimaryPrice(token, true); } return amounts; } function getPositions(address _vault, address _account, address[] memory _collateralTokens, address[] memory _indexTokens, bool[] memory _isLong) public view returns(uint256[] memory) { uint256[] memory amounts = new uint256[](_collateralTokens.length * POSITION_PROPS_LENGTH); for (uint256 i = 0; i < _collateralTokens.length; i++) { { (uint256 size, uint256 collateral, uint256 averagePrice, uint256 entryFundingRate, /* reserveAmount */, uint256 realisedPnl, bool hasRealisedProfit, uint256 lastIncreasedTime) = IVault(_vault).getPosition(_account, _collateralTokens[i], _indexTokens[i], _isLong[i]); amounts[i * POSITION_PROPS_LENGTH] = size; amounts[i * POSITION_PROPS_LENGTH + 1] = collateral; amounts[i * POSITION_PROPS_LENGTH + 2] = averagePrice; amounts[i * POSITION_PROPS_LENGTH + 3] = entryFundingRate; amounts[i * POSITION_PROPS_LENGTH + 4] = hasRealisedProfit ? 1 : 0; amounts[i * POSITION_PROPS_LENGTH + 5] = realisedPnl; amounts[i * POSITION_PROPS_LENGTH + 6] = lastIncreasedTime; } uint256 size = amounts[i * POSITION_PROPS_LENGTH]; uint256 averagePrice = amounts[i * POSITION_PROPS_LENGTH + 2]; uint256 lastIncreasedTime = amounts[i * POSITION_PROPS_LENGTH + 6]; if (averagePrice > 0) { (bool hasProfit, uint256 delta) = IVault(_vault).getDelta(_indexTokens[i], size, averagePrice, _isLong[i], lastIncreasedTime); amounts[i * POSITION_PROPS_LENGTH + 7] = hasProfit ? 1 : 0; amounts[i * POSITION_PROPS_LENGTH + 8] = delta; } } return amounts; } }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPancakeFactory { function getPair(address tokenA, address tokenB) external view returns (address pair); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IPancakeFactory.sol"; contract PancakeFactory is IPancakeFactory { address public btc; address public bnb; address public busd; address public bnbBusdPair; address public btcBnbPair; constructor(address[] memory _addresses) public { btc = _addresses[0]; bnb = _addresses[1]; busd = _addresses[2]; bnbBusdPair = _addresses[3]; btcBnbPair = _addresses[4]; } function getPair(address tokenA, address tokenB) external override view returns (address) { if (tokenA == busd && tokenB == bnb) { return bnbBusdPair; } if (tokenA == bnb && tokenB == btc) { return btcBnbPair; } revert("Invalid tokens"); } }
// SPDX-License-Identifier: MIT import "../libraries/math/SafeMath.sol"; import "./interfaces/IVaultPriceFeed.sol"; import "../oracle/interfaces/IPriceFeed.sol"; import "../oracle/interfaces/ISecondaryPriceFeed.sol"; import "../oracle/interfaces/IChainlinkFlags.sol"; import "../amm/interfaces/IPancakePair.sol"; pragma solidity 0.6.12; contract VaultPriceFeed is IVaultPriceFeed { using SafeMath for uint256; uint256 public constant PRICE_PRECISION = 10 ** 30; uint256 public constant ONE_USD = PRICE_PRECISION; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant MAX_SPREAD_BASIS_POINTS = 50; uint256 public constant MAX_ADJUSTMENT_INTERVAL = 2 hours; uint256 public constant MAX_ADJUSTMENT_BASIS_POINTS = 20; // Identifier of the Sequencer offline flag on the Flags contract address constant private FLAG_ARBITRUM_SEQ_OFFLINE = address(bytes20(bytes32(uint256(keccak256("chainlink.flags.arbitrum-seq-offline")) - 1))); address public gov; address public chainlinkFlags; bool public isAmmEnabled = true; bool public isSecondaryPriceEnabled = true; bool public useV2Pricing = false; bool public favorPrimaryPrice = false; uint256 public priceSampleSpace = 3; uint256 public maxStrictPriceDeviation = 0; address public secondaryPriceFeed; uint256 public spreadThresholdBasisPoints = 30; address public btc; address public eth; address public bnb; address public bnbBusd; address public ethBnb; address public btcBnb; mapping (address => address) public priceFeeds; mapping (address => uint256) public priceDecimals; mapping (address => uint256) public spreadBasisPoints; // Chainlink can return prices for stablecoins // that differs from 1 USD by a larger percentage than stableSwapFeeBasisPoints // we use strictStableTokens to cap the price to 1 USD // this allows us to configure stablecoins like DAI as being a stableToken // while not being a strictStableToken mapping (address => bool) public strictStableTokens; mapping (address => uint256) public override adjustmentBasisPoints; mapping (address => bool) public override isAdjustmentAdditive; mapping (address => uint256) public lastAdjustmentTimings; modifier onlyGov() { require(msg.sender == gov, "VaultPriceFeed: forbidden"); _; } constructor() public { gov = msg.sender; } function setGov(address _gov) external onlyGov { gov = _gov; } function setChainlinkFlags(address _chainlinkFlags) external onlyGov { chainlinkFlags = _chainlinkFlags; } function setAdjustment(address _token, bool _isAdditive, uint256 _adjustmentBps) external override onlyGov { require( lastAdjustmentTimings[_token].add(MAX_ADJUSTMENT_INTERVAL) < block.timestamp, "VaultPriceFeed: adjustment frequency exceeded" ); require(_adjustmentBps <= MAX_ADJUSTMENT_BASIS_POINTS, "invalid _adjustmentBps"); isAdjustmentAdditive[_token] = _isAdditive; adjustmentBasisPoints[_token] = _adjustmentBps; lastAdjustmentTimings[_token] = block.timestamp; } function setUseV2Pricing(bool _useV2Pricing) external override onlyGov { useV2Pricing = _useV2Pricing; } function setIsAmmEnabled(bool _isEnabled) external override onlyGov { isAmmEnabled = _isEnabled; } function setIsSecondaryPriceEnabled(bool _isEnabled) external override onlyGov { isSecondaryPriceEnabled = _isEnabled; } function setSecondaryPriceFeed(address _secondaryPriceFeed) external onlyGov { secondaryPriceFeed = _secondaryPriceFeed; } function setTokens(address _btc, address _eth, address _bnb) external onlyGov { btc = _btc; eth = _eth; bnb = _bnb; } function setPairs(address _bnbBusd, address _ethBnb, address _btcBnb) external onlyGov { bnbBusd = _bnbBusd; ethBnb = _ethBnb; btcBnb = _btcBnb; } function setSpreadBasisPoints(address _token, uint256 _spreadBasisPoints) external override onlyGov { require(_spreadBasisPoints <= MAX_SPREAD_BASIS_POINTS, "VaultPriceFeed: invalid _spreadBasisPoints"); spreadBasisPoints[_token] = _spreadBasisPoints; } function setSpreadThresholdBasisPoints(uint256 _spreadThresholdBasisPoints) external override onlyGov { spreadThresholdBasisPoints = _spreadThresholdBasisPoints; } function setFavorPrimaryPrice(bool _favorPrimaryPrice) external override onlyGov { favorPrimaryPrice = _favorPrimaryPrice; } function setPriceSampleSpace(uint256 _priceSampleSpace) external override onlyGov { require(_priceSampleSpace > 0, "VaultPriceFeed: invalid _priceSampleSpace"); priceSampleSpace = _priceSampleSpace; } function setMaxStrictPriceDeviation(uint256 _maxStrictPriceDeviation) external override onlyGov { maxStrictPriceDeviation = _maxStrictPriceDeviation; } function setTokenConfig( address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable ) external override onlyGov { priceFeeds[_token] = _priceFeed; priceDecimals[_token] = _priceDecimals; strictStableTokens[_token] = _isStrictStable; } function getPrice(address _token, bool _maximise, bool _includeAmmPrice, bool /* _useSwapPricing */) public override view returns (uint256) { uint256 price = useV2Pricing ? getPriceV2(_token, _maximise, _includeAmmPrice) : getPriceV1(_token, _maximise, _includeAmmPrice); uint256 adjustmentBps = adjustmentBasisPoints[_token]; if (adjustmentBps > 0) { bool isAdditive = isAdjustmentAdditive[_token]; if (isAdditive) { price = price.mul(BASIS_POINTS_DIVISOR.add(adjustmentBps)).div(BASIS_POINTS_DIVISOR); } else { price = price.mul(BASIS_POINTS_DIVISOR.sub(adjustmentBps)).div(BASIS_POINTS_DIVISOR); } } return price; } function getPriceV1(address _token, bool _maximise, bool _includeAmmPrice) public view returns (uint256) { uint256 price = getPrimaryPrice(_token, _maximise); if (_includeAmmPrice && isAmmEnabled) { uint256 ammPrice = getAmmPrice(_token); if (ammPrice > 0) { if (_maximise && ammPrice > price) { price = ammPrice; } if (!_maximise && ammPrice < price) { price = ammPrice; } } } if (isSecondaryPriceEnabled) { price = getSecondaryPrice(_token, price, _maximise); } if (strictStableTokens[_token]) { uint256 delta = price > ONE_USD ? price.sub(ONE_USD) : ONE_USD.sub(price); if (delta <= maxStrictPriceDeviation) { return ONE_USD; } // if _maximise and price is e.g. 1.02, return 1.02 if (_maximise && price > ONE_USD) { return price; } // if !_maximise and price is e.g. 0.98, return 0.98 if (!_maximise && price < ONE_USD) { return price; } return ONE_USD; } uint256 _spreadBasisPoints = spreadBasisPoints[_token]; if (_maximise) { return price.mul(BASIS_POINTS_DIVISOR.add(_spreadBasisPoints)).div(BASIS_POINTS_DIVISOR); } return price.mul(BASIS_POINTS_DIVISOR.sub(_spreadBasisPoints)).div(BASIS_POINTS_DIVISOR); } function getPriceV2(address _token, bool _maximise, bool _includeAmmPrice) public view returns (uint256) { uint256 price = getPrimaryPrice(_token, _maximise); if (_includeAmmPrice && isAmmEnabled) { price = getAmmPriceV2(_token, _maximise, price); } if (isSecondaryPriceEnabled) { price = getSecondaryPrice(_token, price, _maximise); } if (strictStableTokens[_token]) { uint256 delta = price > ONE_USD ? price.sub(ONE_USD) : ONE_USD.sub(price); if (delta <= maxStrictPriceDeviation) { return ONE_USD; } // if _maximise and price is e.g. 1.02, return 1.02 if (_maximise && price > ONE_USD) { return price; } // if !_maximise and price is e.g. 0.98, return 0.98 if (!_maximise && price < ONE_USD) { return price; } return ONE_USD; } uint256 _spreadBasisPoints = spreadBasisPoints[_token]; if (_maximise) { return price.mul(BASIS_POINTS_DIVISOR.add(_spreadBasisPoints)).div(BASIS_POINTS_DIVISOR); } return price.mul(BASIS_POINTS_DIVISOR.sub(_spreadBasisPoints)).div(BASIS_POINTS_DIVISOR); } function getAmmPriceV2(address _token, bool _maximise, uint256 _primaryPrice) public view returns (uint256) { uint256 ammPrice = getAmmPrice(_token); if (ammPrice == 0) { return _primaryPrice; } uint256 diff = ammPrice > _primaryPrice ? ammPrice.sub(_primaryPrice) : _primaryPrice.sub(ammPrice); if (diff.mul(BASIS_POINTS_DIVISOR) < _primaryPrice.mul(spreadThresholdBasisPoints)) { if (favorPrimaryPrice) { return _primaryPrice; } return ammPrice; } if (_maximise && ammPrice > _primaryPrice) { return ammPrice; } if (!_maximise && ammPrice < _primaryPrice) { return ammPrice; } return _primaryPrice; } function getLatestPrimaryPrice(address _token) public override view returns (uint256) { address priceFeedAddress = priceFeeds[_token]; require(priceFeedAddress != address(0), "VaultPriceFeed: invalid price feed"); IPriceFeed priceFeed = IPriceFeed(priceFeedAddress); int256 price = priceFeed.latestAnswer(); require(price > 0, "VaultPriceFeed: invalid price"); return uint256(price); } function getPrimaryPrice(address _token, bool _maximise) public override view returns (uint256) { address priceFeedAddress = priceFeeds[_token]; require(priceFeedAddress != address(0), "VaultPriceFeed: invalid price feed"); if (chainlinkFlags != address(0)) { bool isRaised = IChainlinkFlags(chainlinkFlags).getFlag(FLAG_ARBITRUM_SEQ_OFFLINE); if (isRaised) { // If flag is raised we shouldn't perform any critical operations revert("Chainlink feeds are not being updated"); } } IPriceFeed priceFeed = IPriceFeed(priceFeedAddress); uint256 price = 0; uint80 roundId = priceFeed.latestRound(); for (uint80 i = 0; i < priceSampleSpace; i++) { if (roundId <= i) { break; } uint256 p; if (i == 0) { int256 _p = priceFeed.latestAnswer(); require(_p > 0, "VaultPriceFeed: invalid price"); p = uint256(_p); } else { (, int256 _p, , ,) = priceFeed.getRoundData(roundId - i); require(_p > 0, "VaultPriceFeed: invalid price"); p = uint256(_p); } if (price == 0) { price = p; continue; } if (_maximise && p > price) { price = p; continue; } if (!_maximise && p < price) { price = p; } } require(price > 0, "VaultPriceFeed: could not fetch price"); // normalise price precision uint256 _priceDecimals = priceDecimals[_token]; return price.mul(PRICE_PRECISION).div(10 ** _priceDecimals); } function getSecondaryPrice(address _token, uint256 _referencePrice, bool _maximise) public view returns (uint256) { if (secondaryPriceFeed == address(0)) { return _referencePrice; } return ISecondaryPriceFeed(secondaryPriceFeed).getPrice(_token, _referencePrice, _maximise); } function getAmmPrice(address _token) public override view returns (uint256) { if (_token == bnb) { // for bnbBusd, reserve0: BNB, reserve1: BUSD return getPairPrice(bnbBusd, true); } if (_token == eth) { uint256 price0 = getPairPrice(bnbBusd, true); // for ethBnb, reserve0: ETH, reserve1: BNB uint256 price1 = getPairPrice(ethBnb, true); // this calculation could overflow if (price0 / 10**30) * (price1 / 10**30) is more than 10**17 return price0.mul(price1).div(PRICE_PRECISION); } if (_token == btc) { uint256 price0 = getPairPrice(bnbBusd, true); // for btcBnb, reserve0: BTC, reserve1: BNB uint256 price1 = getPairPrice(btcBnb, true); // this calculation could overflow if (price0 / 10**30) * (price1 / 10**30) is more than 10**17 return price0.mul(price1).div(PRICE_PRECISION); } return 0; } // if divByReserve0: calculate price as reserve1 / reserve0 // if !divByReserve1: calculate price as reserve0 / reserve1 function getPairPrice(address _pair, bool _divByReserve0) public view returns (uint256) { (uint256 reserve0, uint256 reserve1, ) = IPancakePair(_pair).getReserves(); if (_divByReserve0) { if (reserve0 == 0) { return 0; } return reserve1.mul(PRICE_PRECISION).div(reserve0); } if (reserve1 == 0) { return 0; } return reserve0.mul(PRICE_PRECISION).div(reserve1); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IPriceFeed { function description() external view returns (string memory); function aggregator() external view returns (address); function latestAnswer() external view returns (int256); function latestRound() external view returns (uint80); function getRoundData(uint80 roundId) external view returns (uint80, int256, uint256, uint256, uint80); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IChainlinkFlags { function getFlag(address) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; interface IPancakePair { function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IPancakePair.sol"; contract PancakePair is IPancakePair { uint112 private reserve0; // uses single storage slot, accessible via getReserves uint112 private reserve1; // uses single storage slot, accessible via getReserves uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves function setReserves(uint256 balance0, uint256 balance1) external { reserve0 = uint112(balance0); reserve1 = uint112(balance1); blockTimestampLast = uint32(block.timestamp); } function getReserves() public override view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) { _reserve0 = reserve0; _reserve1 = reserve1; _blockTimestampLast = blockTimestampLast; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "./interfaces/IPriceFeed.sol"; contract PriceFeed is IPriceFeed { int256 public answer; uint80 public roundId; string public override description = "PriceFeed"; address public override aggregator; uint256 public decimals; address public gov; mapping (uint80 => int256) public answers; mapping (address => bool) public isAdmin; constructor() public { gov = msg.sender; isAdmin[msg.sender] = true; } function setAdmin(address _account, bool _isAdmin) public { require(msg.sender == gov, "PriceFeed: forbidden"); isAdmin[_account] = _isAdmin; } function latestAnswer() public override view returns (int256) { return answer; } function latestRound() public override view returns (uint80) { return roundId; } function setLatestAnswer(int256 _answer) public { require(isAdmin[msg.sender], "PriceFeed: forbidden"); roundId = roundId + 1; answer = _answer; answers[roundId] = _answer; } // returns roundId, answer, startedAt, updatedAt, answeredInRound function getRoundData(uint80 _roundId) public override view returns (uint80, int256, uint256, uint256, uint80) { return (_roundId, answers[_roundId], 0, 0, 0); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "./interfaces/IVault.sol"; import "./interfaces/IVaultUtils.sol"; import "../access/Governable.sol"; contract VaultUtils is IVaultUtils, Governable { using SafeMath for uint256; struct Position { uint256 size; uint256 collateral; uint256 averagePrice; uint256 entryFundingRate; uint256 reserveAmount; int256 realisedPnl; uint256 lastIncreasedTime; } IVault public vault; uint256 public constant BASIS_POINTS_DIVISOR = 10000; uint256 public constant FUNDING_RATE_PRECISION = 1000000; constructor(IVault _vault) public { vault = _vault; } function updateCumulativeFundingRate(address /* _collateralToken */, address /* _indexToken */) public override returns (bool) { return true; } function validateIncreasePosition(address /* _account */, address /* _collateralToken */, address /* _indexToken */, uint256 /* _sizeDelta */, bool /* _isLong */) external override view { // no additional validations } function validateDecreasePosition(address /* _account */, address /* _collateralToken */, address /* _indexToken */ , uint256 /* _collateralDelta */, uint256 /* _sizeDelta */, bool /* _isLong */, address /* _receiver */) external override view { // no additional validations } function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong) internal view returns (Position memory) { IVault _vault = vault; Position memory position; { (uint256 size, uint256 collateral, uint256 averagePrice, uint256 entryFundingRate, /* reserveAmount */, /* realisedPnl */, /* hasProfit */, uint256 lastIncreasedTime) = _vault.getPosition(_account, _collateralToken, _indexToken, _isLong); position.size = size; position.collateral = collateral; position.averagePrice = averagePrice; position.entryFundingRate = entryFundingRate; position.lastIncreasedTime = lastIncreasedTime; } return position; } function validateLiquidation(address _account, address _collateralToken, address _indexToken, bool _isLong, bool _raise) public view override returns (uint256, uint256) { Position memory position = getPosition(_account, _collateralToken, _indexToken, _isLong); IVault _vault = vault; (bool hasProfit, uint256 delta) = _vault.getDelta(_indexToken, position.size, position.averagePrice, _isLong, position.lastIncreasedTime); uint256 marginFees = getFundingFee(_account, _collateralToken, _indexToken, _isLong, position.size, position.entryFundingRate); marginFees = marginFees.add(getPositionFee(_account, _collateralToken, _indexToken, _isLong, position.size)); if (!hasProfit && position.collateral < delta) { if (_raise) { revert("Vault: losses exceed collateral"); } return (1, marginFees); } uint256 remainingCollateral = position.collateral; if (!hasProfit) { remainingCollateral = position.collateral.sub(delta); } if (remainingCollateral < marginFees) { if (_raise) { revert("Vault: fees exceed collateral"); } // cap the fees to the remainingCollateral return (1, remainingCollateral); } if (remainingCollateral < marginFees.add(_vault.liquidationFeeUsd())) { if (_raise) { revert("Vault: liquidation fees exceed collateral"); } return (1, marginFees); } if (remainingCollateral.mul(_vault.maxLeverage()) < position.size.mul(BASIS_POINTS_DIVISOR)) { if (_raise) { revert("Vault: maxLeverage exceeded"); } return (2, marginFees); } return (0, marginFees); } function getEntryFundingRate(address _collateralToken, address /* _indexToken */, bool /* _isLong */) public override view returns (uint256) { return vault.cumulativeFundingRates(_collateralToken); } function getPositionFee(address /* _account */, address /* _collateralToken */, address /* _indexToken */, bool /* _isLong */, uint256 _sizeDelta) public override view returns (uint256) { if (_sizeDelta == 0) { return 0; } uint256 afterFeeUsd = _sizeDelta.mul(BASIS_POINTS_DIVISOR.sub(vault.marginFeeBasisPoints())).div(BASIS_POINTS_DIVISOR); return _sizeDelta.sub(afterFeeUsd); } function getFundingFee(address /* _account */, address _collateralToken, address /* _indexToken */, bool /* _isLong */, uint256 _size, uint256 _entryFundingRate) public override view returns (uint256) { if (_size == 0) { return 0; } uint256 fundingRate = vault.cumulativeFundingRates(_collateralToken).sub(_entryFundingRate); if (fundingRate == 0) { return 0; } return _size.mul(fundingRate).div(FUNDING_RATE_PRECISION); } function getBuyUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) public override view returns (uint256) { return getFeeBasisPoints(_token, _usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), true); } function getSellUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) public override view returns (uint256) { return getFeeBasisPoints(_token, _usdgAmount, vault.mintBurnFeeBasisPoints(), vault.taxBasisPoints(), false); } function getSwapFeeBasisPoints(address _tokenIn, address _tokenOut, uint256 _usdgAmount) public override view returns (uint256) { bool isStableSwap = vault.stableTokens(_tokenIn) && vault.stableTokens(_tokenOut); uint256 baseBps = isStableSwap ? vault.stableSwapFeeBasisPoints() : vault.swapFeeBasisPoints(); uint256 taxBps = isStableSwap ? vault.stableTaxBasisPoints() : vault.taxBasisPoints(); uint256 feesBasisPoints0 = getFeeBasisPoints(_tokenIn, _usdgAmount, baseBps, taxBps, true); uint256 feesBasisPoints1 = getFeeBasisPoints(_tokenOut, _usdgAmount, baseBps, taxBps, false); // use the higher of the two fee basis points return feesBasisPoints0 > feesBasisPoints1 ? feesBasisPoints0 : feesBasisPoints1; } // cases to consider // 1. initialAmount is far from targetAmount, action increases balance slightly => high rebate // 2. initialAmount is far from targetAmount, action increases balance largely => high rebate // 3. initialAmount is close to targetAmount, action increases balance slightly => low rebate // 4. initialAmount is far from targetAmount, action reduces balance slightly => high tax // 5. initialAmount is far from targetAmount, action reduces balance largely => high tax // 6. initialAmount is close to targetAmount, action reduces balance largely => low tax // 7. initialAmount is above targetAmount, nextAmount is below targetAmount and vice versa // 8. a large swap should have similar fees as the same trade split into multiple smaller swaps function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) public override view returns (uint256) { if (!vault.hasDynamicFees()) { return _feeBasisPoints; } uint256 initialAmount = vault.usdgAmounts(_token); uint256 nextAmount = initialAmount.add(_usdgDelta); if (!_increment) { nextAmount = _usdgDelta > initialAmount ? 0 : initialAmount.sub(_usdgDelta); } uint256 targetAmount = vault.getTargetUsdgAmount(_token); if (targetAmount == 0) { return _feeBasisPoints; } uint256 initialDiff = initialAmount > targetAmount ? initialAmount.sub(targetAmount) : targetAmount.sub(initialAmount); uint256 nextDiff = nextAmount > targetAmount ? nextAmount.sub(targetAmount) : targetAmount.sub(nextAmount); // action improves relative asset balance if (nextDiff < initialDiff) { uint256 rebateBps = _taxBasisPoints.mul(initialDiff).div(targetAmount); return rebateBps > _feeBasisPoints ? 0 : _feeBasisPoints.sub(rebateBps); } uint256 averageDiff = initialDiff.add(nextDiff).div(2); if (averageDiff > targetAmount) { averageDiff = targetAmount; } uint256 taxBps = _taxBasisPoints.mul(averageDiff).div(targetAmount); return _feeBasisPoints.add(taxBps); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../Vault.sol"; contract VaultTest is Vault { function increaseGlobalShortSize(address token, uint256 amount) external { _increaseGlobalShortSize(token, amount); } }
// SPDX-License-Identifier: MIT import "../libraries/math/SafeMath.sol"; import "./interfaces/IFastPriceEvents.sol"; import "../access/Governable.sol"; pragma solidity 0.6.12; contract FastPriceEvents is IFastPriceEvents, Governable { mapping (address => bool) public isPriceFeed; event PriceUpdate(address token, uint256 price, address priceFeed); function setIsPriceFeed(address _priceFeed, bool _isPriceFeed) external onlyGov { isPriceFeed[_priceFeed] = _isPriceFeed; } function emitPriceEvent(address _token, uint256 _price) external override { require(isPriceFeed[msg.sender], "FastPriceEvents: invalid sender"); emit PriceUpdate(_token, _price, msg.sender); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../core/interfaces/IVault.sol"; contract BalanceUpdater { using SafeMath for uint256; function updateBalance( address _vault, address _token, address _usdg, uint256 _usdgAmount ) public { IVault vault = IVault(_vault); IERC20 token = IERC20(_token); uint256 poolAmount = vault.poolAmounts(_token); uint256 fee = vault.feeReserves(_token); uint256 balance = token.balanceOf(_vault); uint256 transferAmount = poolAmount.add(fee).sub(balance); token.transferFrom(msg.sender, _vault, transferAmount); IERC20(_usdg).transferFrom(msg.sender, _vault, _usdgAmount); vault.sellUSDG(_token, msg.sender); } }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IAmmRouter.sol"; import "./interfaces/IGmxMigrator.sol"; import "../core/interfaces/IVault.sol"; contract MigrationHandler is ReentrancyGuard { using SafeMath for uint256; uint256 public constant USDG_PRECISION = 10 ** 18; bool public isInitialized; address public admin; address public ammRouterV1; address public ammRouterV2; address public vault; address public gmt; address public xgmt; address public usdg; address public bnb; address public busd; mapping (address => mapping (address => uint256)) public refundedAmounts; modifier onlyAdmin() { require(msg.sender == admin, "MigrationHandler: forbidden"); _; } constructor() public { admin = msg.sender; } function initialize( address _ammRouterV1, address _ammRouterV2, address _vault, address _gmt, address _xgmt, address _usdg, address _bnb, address _busd ) public onlyAdmin { require(!isInitialized, "MigrationHandler: already initialized"); isInitialized = true; ammRouterV1 = _ammRouterV1; ammRouterV2 = _ammRouterV2; vault = _vault; gmt = _gmt; xgmt = _xgmt; usdg = _usdg; bnb = _bnb; busd = _busd; } function redeemUsdg( address _migrator, address _redemptionToken, uint256 _usdgAmount ) external onlyAdmin nonReentrant { IERC20(usdg).transferFrom(_migrator, vault, _usdgAmount); uint256 amount = IVault(vault).sellUSDG(_redemptionToken, address(this)); address[] memory path = new address[](2); path[0] = bnb; path[1] = busd; if (_redemptionToken != bnb) { path = new address[](3); path[0] = _redemptionToken; path[1] = bnb; path[2] = busd; } IERC20(_redemptionToken).approve(ammRouterV2, amount); IAmmRouter(ammRouterV2).swapExactTokensForTokens( amount, 0, path, _migrator, block.timestamp ); } function swap( address _migrator, uint256 _gmtAmountForUsdg, uint256 _xgmtAmountForUsdg, uint256 _gmtAmountForBusd ) external onlyAdmin nonReentrant { address[] memory path = new address[](2); path[0] = gmt; path[1] = usdg; IERC20(gmt).transferFrom(_migrator, address(this), _gmtAmountForUsdg); IERC20(gmt).approve(ammRouterV2, _gmtAmountForUsdg); IAmmRouter(ammRouterV2).swapExactTokensForTokens( _gmtAmountForUsdg, 0, path, _migrator, block.timestamp ); path[0] = xgmt; path[1] = usdg; IERC20(xgmt).transferFrom(_migrator, address(this), _xgmtAmountForUsdg); IERC20(xgmt).approve(ammRouterV2, _xgmtAmountForUsdg); IAmmRouter(ammRouterV2).swapExactTokensForTokens( _xgmtAmountForUsdg, 0, path, _migrator, block.timestamp ); path[0] = gmt; path[1] = busd; IERC20(gmt).transferFrom(_migrator, address(this), _gmtAmountForBusd); IERC20(gmt).approve(ammRouterV1, _gmtAmountForBusd); IAmmRouter(ammRouterV1).swapExactTokensForTokens( _gmtAmountForBusd, 0, path, _migrator, block.timestamp ); } function refund( address _migrator, address _account, address _token, uint256 _usdgAmount ) external onlyAdmin nonReentrant { address iouToken = IGmxMigrator(_migrator).iouTokens(_token); uint256 iouBalance = IERC20(iouToken).balanceOf(_account); uint256 iouTokenAmount = _usdgAmount.div(2); // each GMX is priced at $2 uint256 refunded = refundedAmounts[_account][iouToken]; refundedAmounts[_account][iouToken] = refunded.add(iouTokenAmount); require(refundedAmounts[_account][iouToken] <= iouBalance, "MigrationHandler: refundable amount exceeded"); IERC20(usdg).transferFrom(_migrator, _account, _usdgAmount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IAmmRouter { function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IGmxMigrator { function iouTokens(address _token) external view returns (address); }
//SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "../libraries/utils/ReentrancyGuard.sol"; import "./interfaces/IGmxIou.sol"; import "./interfaces/IAmmRouter.sol"; import "./interfaces/IGmxMigrator.sol"; contract GmxMigrator is ReentrancyGuard, IGmxMigrator { using SafeMath for uint256; bool public isInitialized; bool public isMigrationActive = true; bool public hasMaxMigrationLimit = false; uint256 public minAuthorizations; address public ammRouter; uint256 public gmxPrice; uint256 public actionsNonce; address public admin; address[] public signers; mapping (address => bool) public isSigner; mapping (bytes32 => bool) public pendingActions; mapping (address => mapping (bytes32 => bool)) public signedActions; mapping (address => bool) public whitelistedTokens; mapping (address => address) public override iouTokens; mapping (address => uint256) public prices; mapping (address => uint256) public caps; mapping (address => bool) public lpTokens; mapping (address => address) public lpTokenAs; mapping (address => address) public lpTokenBs; mapping (address => uint256) public tokenAmounts; mapping (address => mapping (address => uint256)) public migratedAmounts; mapping (address => mapping (address => uint256)) public maxMigrationAmounts; event SignalApprove(address token, address spender, uint256 amount, bytes32 action, uint256 nonce); event SignalPendingAction(bytes32 action, uint256 nonce); event SignAction(bytes32 action, uint256 nonce); event ClearAction(bytes32 action, uint256 nonce); constructor(uint256 _minAuthorizations) public { admin = msg.sender; minAuthorizations = _minAuthorizations; } modifier onlyAdmin() { require(msg.sender == admin, "GmxMigrator: forbidden"); _; } modifier onlySigner() { require(isSigner[msg.sender], "GmxMigrator: forbidden"); _; } function initialize( address _ammRouter, uint256 _gmxPrice, address[] memory _signers, address[] memory _whitelistedTokens, address[] memory _iouTokens, uint256[] memory _prices, uint256[] memory _caps, address[] memory _lpTokens, address[] memory _lpTokenAs, address[] memory _lpTokenBs ) public onlyAdmin { require(!isInitialized, "GmxMigrator: already initialized"); require(_whitelistedTokens.length == _iouTokens.length, "GmxMigrator: invalid _iouTokens.length"); require(_whitelistedTokens.length == _prices.length, "GmxMigrator: invalid _prices.length"); require(_whitelistedTokens.length == _caps.length, "GmxMigrator: invalid _caps.length"); require(_lpTokens.length == _lpTokenAs.length, "GmxMigrator: invalid _lpTokenAs.length"); require(_lpTokens.length == _lpTokenBs.length, "GmxMigrator: invalid _lpTokenBs.length"); isInitialized = true; ammRouter = _ammRouter; gmxPrice = _gmxPrice; signers = _signers; for (uint256 i = 0; i < _signers.length; i++) { address signer = _signers[i]; isSigner[signer] = true; } for (uint256 i = 0; i < _whitelistedTokens.length; i++) { address token = _whitelistedTokens[i]; whitelistedTokens[token] = true; iouTokens[token] = _iouTokens[i]; prices[token] = _prices[i]; caps[token] = _caps[i]; } for (uint256 i = 0; i < _lpTokens.length; i++) { address token = _lpTokens[i]; lpTokens[token] = true; lpTokenAs[token] = _lpTokenAs[i]; lpTokenBs[token] = _lpTokenBs[i]; } } function endMigration() public onlyAdmin { isMigrationActive = false; } function setHasMaxMigrationLimit(bool _hasMaxMigrationLimit) public onlyAdmin { hasMaxMigrationLimit = _hasMaxMigrationLimit; } function setMaxMigrationAmount(address _account, address _token, uint256 _maxMigrationAmount) public onlyAdmin { maxMigrationAmounts[_account][_token] = _maxMigrationAmount; } function migrate( address _token, uint256 _tokenAmount ) public nonReentrant { require(isMigrationActive, "GmxMigrator: migration is no longer active"); require(whitelistedTokens[_token], "GmxMigrator: token not whitelisted"); require(_tokenAmount > 0, "GmxMigrator: invalid tokenAmount"); if (hasMaxMigrationLimit) { migratedAmounts[msg.sender][_token] = migratedAmounts[msg.sender][_token].add(_tokenAmount); require(migratedAmounts[msg.sender][_token] <= maxMigrationAmounts[msg.sender][_token], "GmxMigrator: maxMigrationAmount exceeded"); } uint256 tokenPrice = getTokenPrice(_token); uint256 mintAmount = _tokenAmount.mul(tokenPrice).div(gmxPrice); require(mintAmount > 0, "GmxMigrator: invalid mintAmount"); tokenAmounts[_token] = tokenAmounts[_token].add(_tokenAmount); require(tokenAmounts[_token] < caps[_token], "GmxMigrator: token cap exceeded"); IERC20(_token).transferFrom(msg.sender, address(this), _tokenAmount); if (lpTokens[_token]) { address tokenA = lpTokenAs[_token]; address tokenB = lpTokenBs[_token]; require(tokenA != address(0), "GmxMigrator: invalid tokenA"); require(tokenB != address(0), "GmxMigrator: invalid tokenB"); IERC20(_token).approve(ammRouter, _tokenAmount); IAmmRouter(ammRouter).removeLiquidity(tokenA, tokenB, _tokenAmount, 0, 0, address(this), block.timestamp); } address iouToken = getIouToken(_token); IGmxIou(iouToken).mint(msg.sender, mintAmount); } function signalApprove(address _token, address _spender, uint256 _amount) external nonReentrant onlyAdmin { actionsNonce++; uint256 nonce = actionsNonce; bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, nonce)); _setPendingAction(action, nonce); emit SignalApprove(_token, _spender, _amount, action, nonce); } function signApprove(address _token, address _spender, uint256 _amount, uint256 _nonce) external nonReentrant onlySigner { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, _nonce)); _validateAction(action); require(!signedActions[msg.sender][action], "GmxMigrator: already signed"); signedActions[msg.sender][action] = true; emit SignAction(action, _nonce); } function approve(address _token, address _spender, uint256 _amount, uint256 _nonce) external nonReentrant onlyAdmin { bytes32 action = keccak256(abi.encodePacked("approve", _token, _spender, _amount, _nonce)); _validateAction(action); _validateAuthorization(action); IERC20(_token).approve(_spender, _amount); _clearAction(action, _nonce); } function getTokenAmounts(address[] memory _tokens) public view returns (uint256[] memory) { uint256[] memory amounts = new uint256[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { address token = _tokens[i]; amounts[i] = tokenAmounts[token]; } return amounts; } function getTokenPrice(address _token) public view returns (uint256) { uint256 price = prices[_token]; require(price != 0, "GmxMigrator: invalid token price"); return price; } function getIouToken(address _token) public view returns (address) { address iouToken = iouTokens[_token]; require(iouToken != address(0), "GmxMigrator: invalid iou token"); return iouToken; } function _setPendingAction(bytes32 _action, uint256 _nonce) private { pendingActions[_action] = true; emit SignalPendingAction(_action, _nonce); } function _validateAction(bytes32 _action) private view { require(pendingActions[_action], "GmxMigrator: action not signalled"); } function _validateAuthorization(bytes32 _action) private view { uint256 count = 0; for (uint256 i = 0; i < signers.length; i++) { address signer = signers[i]; if (signedActions[signer][_action]) { count++; } } if (count == 0) { revert("GmxMigrator: action not authorized"); } require(count >= minAuthorizations, "GmxMigrator: insufficient authorization"); } function _clearAction(bytes32 _action, uint256 _nonce) private { require(pendingActions[_action], "GmxMigrator: invalid _action"); delete pendingActions[_action]; emit ClearAction(_action, _nonce); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; interface IGmxIou { function mint(address account, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "./interfaces/IGmxIou.sol"; contract GmxIou is IERC20, IGmxIou { using SafeMath for uint256; mapping (address => uint256) private _balances; uint256 public override totalSupply; string public name; string public symbol; uint8 public decimals; address public minter; constructor (address _minter, string memory _name, string memory _symbol) public { name = _name; symbol = _symbol; minter = _minter; decimals = 18; } function mint(address account, uint256 amount) public override returns (bool) { require(msg.sender == minter, "GmxIou: forbidden"); _mint(account, amount); return true; } function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } // empty implementation, GmxIou tokens are non-transferrable function transfer(address /* recipient */, uint256 /* amount */) public override returns (bool) { revert("GmxIou: non-transferrable"); } // empty implementation, GmxIou tokens are non-transferrable function allowance(address /* owner */, address /* spender */) public view virtual override returns (uint256) { return 0; } // empty implementation, GmxIou tokens are non-transferrable function approve(address /* spender */, uint256 /* amount */) public virtual override returns (bool) { revert("GmxIou: non-transferrable"); } // empty implementation, GmxIou tokens are non-transferrable function transferFrom(address /* sender */, address /* recipient */, uint256 /* amount */) public virtual override returns (bool) { revert("GmxIou: non-transferrable"); } function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "GmxIou: mint to the zero address"); totalSupply = totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./interfaces/IVault.sol"; import "../access/Governable.sol"; contract VaultErrorController is Governable { function setErrors(IVault _vault, string[] calldata _errors) external onlyGov { for (uint256 i = 0; i < _errors.length; i++) { _vault.setError(i, _errors[i]); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../tokens/MintableBaseToken.sol"; contract GMX is MintableBaseToken { constructor() public MintableBaseToken("GMX", "GMX", 0) { } function id() external pure returns (string memory _name) { return "GMX"; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../tokens/MintableBaseToken.sol"; contract GLP is MintableBaseToken { constructor() public MintableBaseToken("GMX LP", "GLP", 0) { } function id() external pure returns (string memory _name) { return "GLP"; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../tokens/MintableBaseToken.sol"; contract EsGMX is MintableBaseToken { constructor() public MintableBaseToken("Escrowed GMX", "esGMX", 0) { } function id() external pure returns (string memory _name) { return "esGMX"; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/math/SafeMath.sol"; import "../libraries/token/IERC20.sol"; import "./interfaces/IGMT.sol"; import "../peripherals/interfaces/ITimelockTarget.sol"; contract GMT is IERC20, IGMT, ITimelockTarget { using SafeMath for uint256; string public constant name = "Gambit"; string public constant symbol = "GMT"; uint8 public constant decimals = 18; uint256 public override totalSupply; address public gov; bool public hasActiveMigration; uint256 public migrationTime; mapping (address => uint256) public balances; mapping (address => mapping (address => uint256)) public allowances; mapping (address => bool) public admins; // only checked when hasActiveMigration is true // this can be used to block the AMM pair as a recipient // and protect liquidity providers during a migration // by disabling the selling of GMT mapping (address => bool) public blockedRecipients; // only checked when hasActiveMigration is true // this can be used for: // - only allowing tokens to be transferred by the distribution contract // during the initial distribution phase, this would prevent token buyers // from adding liquidity before the initial liquidity is seeded // - only allowing removal of GMT liquidity and no other actions // during the migration phase mapping (address => bool) public allowedMsgSenders; modifier onlyGov() { require(msg.sender == gov, "GMT: forbidden"); _; } modifier onlyAdmin() { require(admins[msg.sender], "GMT: forbidden"); _; } constructor(uint256 _initialSupply) public { gov = msg.sender; admins[msg.sender] = true; _mint(msg.sender, _initialSupply); } function setGov(address _gov) external override onlyGov { gov = _gov; } function addAdmin(address _account) external onlyGov { admins[_account] = true; } function removeAdmin(address _account) external onlyGov { admins[_account] = false; } function setNextMigrationTime(uint256 _migrationTime) external onlyGov { require(_migrationTime > migrationTime, "GMT: invalid _migrationTime"); migrationTime = _migrationTime; } function beginMigration() external override onlyAdmin { require(block.timestamp > migrationTime, "GMT: migrationTime not yet passed"); hasActiveMigration = true; } function endMigration() external override onlyAdmin { hasActiveMigration = false; } function addBlockedRecipient(address _recipient) external onlyGov { blockedRecipients[_recipient] = true; } function removeBlockedRecipient(address _recipient) external onlyGov { blockedRecipients[_recipient] = false; } function addMsgSender(address _msgSender) external onlyGov { allowedMsgSenders[_msgSender] = true; } function removeMsgSender(address _msgSender) external onlyGov { allowedMsgSenders[_msgSender] = false; } // to help users who accidentally send their tokens to this contract function withdrawToken(address _token, address _account, uint256 _amount) external override onlyGov { IERC20(_token).transfer(_account, _amount); } function balanceOf(address _account) external view override returns (uint256) { return balances[_account]; } function transfer(address _recipient, uint256 _amount) external override returns (bool) { _transfer(msg.sender, _recipient, _amount); return true; } function allowance(address _owner, address _spender) external view override returns (uint256) { return allowances[_owner][_spender]; } function approve(address _spender, uint256 _amount) external override returns (bool) { _approve(msg.sender, _spender, _amount); return true; } function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) { uint256 nextAllowance = allowances[_sender][msg.sender].sub(_amount, "GMT: transfer amount exceeds allowance"); _approve(_sender, msg.sender, nextAllowance); _transfer(_sender, _recipient, _amount); return true; } function _transfer(address _sender, address _recipient, uint256 _amount) private { require(_sender != address(0), "GMT: transfer from the zero address"); require(_recipient != address(0), "GMT: transfer to the zero address"); if (hasActiveMigration) { require(allowedMsgSenders[msg.sender], "GMT: forbidden msg.sender"); require(!blockedRecipients[_recipient], "GMT: forbidden recipient"); } balances[_sender] = balances[_sender].sub(_amount, "GMT: transfer amount exceeds balance"); balances[_recipient] = balances[_recipient].add(_amount); emit Transfer(_sender, _recipient,_amount); } function _mint(address _account, uint256 _amount) private { require(_account != address(0), "GMT: mint to the zero address"); totalSupply = totalSupply.add(_amount); balances[_account] = balances[_account].add(_amount); emit Transfer(address(0), _account, _amount); } function _approve(address _owner, address _spender, uint256 _amount) private { require(_owner != address(0), "GMT: approve from the zero address"); require(_spender != address(0), "GMT: approve to the zero address"); allowances[_owner][_spender] = _amount; emit Approval(_owner, _spender, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract FaucetToken is IERC20 { using SafeMath for uint256; uint256 public DROPLET_INTERVAL = 8 hours; address public _gov; uint256 public _dropletAmount; bool public _isFaucetEnabled; mapping (address => uint256) public _claimedAt; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor( string memory name, string memory symbol, uint8 decimals, uint256 dropletAmount ) public { _name = name; _symbol = symbol; _decimals = decimals; _gov = msg.sender; _dropletAmount = dropletAmount; } function mint(address account, uint256 amount) public { require(msg.sender == _gov, "FaucetToken: forbidden"); _mint(account, amount); } function enableFaucet() public { require(msg.sender == _gov, "FaucetToken: forbidden"); _isFaucetEnabled = true; } function disableFaucet() public { require(msg.sender == _gov, "FaucetToken: forbidden"); _isFaucetEnabled = false; } function setDropletAmount(uint256 dropletAmount) public { require(msg.sender == _gov, "FaucetToken: forbidden"); _dropletAmount = dropletAmount; } function claimDroplet() public { require(_isFaucetEnabled, "FaucetToken: faucet not enabled"); require(_claimedAt[msg.sender].add(DROPLET_INTERVAL) <= block.timestamp, "FaucetToken: droplet not available yet"); _claimedAt[msg.sender] = block.timestamp; _mint(msg.sender, _dropletAmount); } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } }
// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import "../libraries/token/IERC20.sol"; import "../libraries/math/SafeMath.sol"; import "../access/Governable.sol"; contract BatchSender is Governable { using SafeMath for uint256; mapping (address => bool) public isHandler; event BatchSend( uint256 indexed typeId, address indexed token, address[] accounts, uint256[] amounts ); modifier onlyHandler() { require(isHandler[msg.sender], "BatchSender: forbidden"); _; } constructor() public { isHandler[msg.sender] = true; } function setHandler(address _handler, bool _isActive) external onlyGov { isHandler[_handler] = _isActive; } function send(IERC20 _token, address[] memory _accounts, uint256[] memory _amounts) public onlyHandler { _send(_token, _accounts, _amounts, 0); } function sendAndEmit(IERC20 _token, address[] memory _accounts, uint256[] memory _amounts, uint256 _typeId) public onlyHandler { _send(_token, _accounts, _amounts, _typeId); } function _send(IERC20 _token, address[] memory _accounts, uint256[] memory _amounts, uint256 _typeId) private { for (uint256 i = 0; i < _accounts.length; i++) { address account = _accounts[i]; uint256 amount = _amounts[i]; _token.transferFrom(msg.sender, account, amount); } emit BatchSend(_typeId, address(_token), _accounts, _amounts); } }
{ "optimizer": { "enabled": true, "runs": 10 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdgAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeBasisPoints","type":"uint256"}],"name":"BuyUSDG","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"averagePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"entryFundingRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"indexed":false,"internalType":"int256","name":"realisedPnl","type":"int256"}],"name":"ClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeUsd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeTokens","type":"uint256"}],"name":"CollectMarginFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeUsd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeTokens","type":"uint256"}],"name":"CollectSwapFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DecreaseGuaranteedUsd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DecreasePoolAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"DecreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DecreaseReservedAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DecreaseUsdgAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DirectPoolDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncreaseGuaranteedUsd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncreasePoolAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sizeDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"IncreasePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncreaseReservedAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncreaseUsdgAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"collateralToken","type":"address"},{"indexed":false,"internalType":"address","name":"indexToken","type":"address"},{"indexed":false,"internalType":"bool","name":"isLong","type":"bool"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"indexed":false,"internalType":"int256","name":"realisedPnl","type":"int256"},{"indexed":false,"internalType":"uint256","name":"markPrice","type":"uint256"}],"name":"LiquidatePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"usdgAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeBasisPoints","type":"uint256"}],"name":"SellUSDG","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOutAfterFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeBasisPoints","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"fundingRate","type":"uint256"}],"name":"UpdateFundingRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"hasProfit","type":"bool"},{"indexed":false,"internalType":"uint256","name":"delta","type":"uint256"}],"name":"UpdatePnl","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"averagePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"entryFundingRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"indexed":false,"internalType":"int256","name":"realisedPnl","type":"int256"},{"indexed":false,"internalType":"uint256","name":"markPrice","type":"uint256"}],"name":"UpdatePosition","type":"event"},{"inputs":[],"name":"BASIS_POINTS_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FUNDING_RATE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FUNDING_RATE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_LIQUIDATION_FEE_USD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FUNDING_RATE_INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LEVERAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDG_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"addRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenDiv","type":"address"},{"internalType":"address","name":"_tokenMul","type":"address"}],"name":"adjustForDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allWhitelistedTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allWhitelistedTokensLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"approvedRouters","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bufferAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"buyUSDG","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"clearTokenConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cumulativeFundingRates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"decreasePosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"directPoolDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"errorController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"errors","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingRateFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_size","type":"uint256"},{"internalType":"uint256","name":"_averagePrice","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_lastIncreasedTime","type":"uint256"}],"name":"getDelta","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getEntryFundingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdgDelta","type":"uint256"},{"internalType":"uint256","name":"_feeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_taxBasisPoints","type":"uint256"},{"internalType":"bool","name":"_increment","type":"bool"}],"name":"getFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_size","type":"uint256"},{"internalType":"uint256","name":"_entryFundingRate","type":"uint256"}],"name":"getFundingFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getGlobalShortDelta","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getMaxPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getMinPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_size","type":"uint256"},{"internalType":"uint256","name":"_averagePrice","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"uint256","name":"_lastIncreasedTime","type":"uint256"}],"name":"getNextAveragePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getNextFundingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_nextPrice","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"}],"name":"getNextGlobalShortAveragePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPositionDelta","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"}],"name":"getPositionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPositionKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPositionLeverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdgAmount","type":"uint256"}],"name":"getRedemptionAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getRedemptionCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getRedemptionCollateralUsd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTargetUsdgAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getUtilisation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"globalShortAveragePrices","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"globalShortSizes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"guaranteedUsd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasDynamicFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inManagerMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inPrivateLiquidationMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"includeAmmPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"increasePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_usdg","type":"address"},{"internalType":"address","name":"_priceFeed","type":"address"},{"internalType":"uint256","name":"_liquidationFeeUsd","type":"uint256"},{"internalType":"uint256","name":"_fundingRateFactor","type":"uint256"},{"internalType":"uint256","name":"_stableFundingRateFactor","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLeverageEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isLiquidator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSwapEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastFundingTimes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"liquidatePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationFeeUsd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marginFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxGlobalShortSizes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLeverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxUsdgAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minProfitBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minProfitTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintBurnFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"positions","outputs":[{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"averagePrice","type":"uint256"},{"internalType":"uint256","name":"entryFundingRate","type":"uint256"},{"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"internalType":"int256","name":"realisedPnl","type":"int256"},{"internalType":"uint256","name":"lastIncreasedTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"removeRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"reservedAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"sellUSDG","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setBufferAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_errorCode","type":"uint256"},{"internalType":"string","name":"_error","type":"string"}],"name":"setError","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_errorController","type":"address"}],"name":"setErrorController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_taxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_stableTaxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_mintBurnFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_swapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_stableSwapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_marginFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_liquidationFeeUsd","type":"uint256"},{"internalType":"uint256","name":"_minProfitTime","type":"uint256"},{"internalType":"bool","name":"_hasDynamicFees","type":"bool"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fundingInterval","type":"uint256"},{"internalType":"uint256","name":"_fundingRateFactor","type":"uint256"},{"internalType":"uint256","name":"_stableFundingRateFactor","type":"uint256"}],"name":"setFundingRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gov","type":"address"}],"name":"setGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_inManagerMode","type":"bool"}],"name":"setInManagerMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_inPrivateLiquidationMode","type":"bool"}],"name":"setInPrivateLiquidationMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isLeverageEnabled","type":"bool"}],"name":"setIsLeverageEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isSwapEnabled","type":"bool"}],"name":"setIsSwapEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setLiquidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bool","name":"_isManager","type":"bool"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGasPrice","type":"uint256"}],"name":"setMaxGasPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setMaxGlobalShortSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxLeverage","type":"uint256"}],"name":"setMaxLeverage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeed","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenDecimals","type":"uint256"},{"internalType":"uint256","name":"_tokenWeight","type":"uint256"},{"internalType":"uint256","name":"_minProfitBps","type":"uint256"},{"internalType":"uint256","name":"_maxUsdgAmount","type":"uint256"},{"internalType":"bool","name":"_isStable","type":"bool"},{"internalType":"bool","name":"_isShortable","type":"bool"}],"name":"setTokenConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setUsdgAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IVaultUtils","name":"_vaultUtils","type":"address"}],"name":"setVaultUtils","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"shortableTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableFundingRateFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableSwapFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableTaxBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stableTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"tokenToUsdMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenWeights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokenWeights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"}],"name":"updateCumulativeFundingRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newVault","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"upgradeVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdAmount","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"usdToToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdAmount","type":"uint256"}],"name":"usdToTokenMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdAmount","type":"uint256"}],"name":"usdToTokenMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdg","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"usdgAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"useSwapPricing","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"bool","name":"_raise","type":"bool"}],"name":"validateLiquidation","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultUtils","outputs":[{"internalType":"contract IVaultUtils","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistedTokenCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"withdrawFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040526001805462ff00001961ff00199091166101001716620100001781556207a1206008556032600a9081556014600b55601e600c819055600d556004600e55600f556011805460ff199081169091556170806012556016805463ffffff0019921690921716905534801561007657600080fd5b506001600055600680546001600160a01b03191633179055615f0d806200009e6000396000f3fe608060405234801561001057600080fd5b50600436106105995760003560e01c80638585f4d2116102e9578063c7e074c31161018f578063c7e074c314611225578063cea0c32814611265578063cfad57a21461129b578063d2fa635e146112c1578063d3127e63146112de578063d54d5a9f146112fb578063d66b000d1461135a578063d8f897c314611386578063d9ac4225146113ac578063da76524c146113b4578063daf9c210146113fe578063db3555fb14611424578063db97495f1461144a578063dc8f5fac14611496578063de2ea9481461149e578063df73a267146114e6578063e124e6d2146114ee578063e468baf014611514578063e67f59a714611531578063efa10a6e14611557578063f07456ce14611583578063f07bbf77146115a9578063f2555278146115c8578063f3ae2415146115f6578063f5b91b7b1461161c578063f887ea4014611624578063fa12dbc01461162c578063fbfded6d1461165e578063fce28c101461168c578063fdaf6ac314611694578063fed1a606146116d857610599565b80638585f4d214610e90578063870d917c14610ebc57806388b1fbdf14610ec45780638a27d46814610eea5780638a39735a1461061a5780638a78daa814610f135780638ee573ac14610f395780638f7b840414610f5f5780639060b1ca14610f855780639331621214610f8d57806395082d2514610fc55780639698d25a14610fcd5780639849e41214610ff35780639899cd0214610ffb5780639d7432ca146110275780639f392eb314611059578063a22f239214611061578063a42ab3d214611069578063a5e90eee14611095578063a93acac2146110c3578063ab08c1c6146110e9578063ab2f3ad4146110f1578063ad1e4f8d14611117578063ae3302c21461113d578063b06423f314611145578063b136ca491461114d578063b1cc53ab14611173578063b364accb146111ab578063c3c7b9e9146111d1578063c4f718bf146111f7578063c65bc7b1146111ff57610599565b80634453a3741161044e5780634453a3741461097057806345a6f3701461099e57806348d91abf146109f757806348f35cbb14610a3b5780634a3f088d14610a435780634a993ee914610ac45780634befe2ca14610aea5780634d47b30414610af2578063514ea4bf14610afa57806351723e8214610b4f578063523fba7f14610b8d578063529a356f14610bb357806352f55eed14610bd95780635c07eaab14610bff5780635f7bc11914610c3f5780636092219914610c6557806362287a3214610c935780636274980314610c9b5780636abbe0c814610cc15780636ae0b15414610cc95780636be6026b14610cef57806371089f4d14610cf7578063711e619014610d1d578063724e78da14610d4b578063728cdbca14610d71578063741bef1a14610db95780637a210a2b14610dc15780637c2eb9f714610dc9578063817bb85714610de857806381a612d614610e1657806382a0849014610e3c57610599565b806304fef1db1461059e57806307c58752146105d65780630842b076146105de5780630a48d5a9146105e657806310eb56c214610612578063126082cf1461061a57806312d43a5114610622578063134ca63b14610646578063174d26941461064e578063181e210e146106565780631aa4ace5146106725780631ce9cb8f1461069857806324b0c04d146106be57806324ca984e146106df57806328e67be51461070557806329ff96151461077a5780632c668ec1146107a05780632d4b0576146107cc57806330455ede1461080a578063318bc6891461082957806334c1557d1461061a578063351a964d14610831578063392e53cd146108395780633a05dcc1146108415780633c5a6e35146108675780633de39c11146108b55780633e72a262146108bd57806340eb3802146108c5578063421528731461091657806342b60b031461094a575b600080fd5b6105c4600480360360208110156105b457600080fd5b50356001600160a01b031661176a565b60408051918252519081900360200190f35b6105c46117cd565b6105c46117de565b6105c4600480360360408110156105fc57600080fd5b506001600160a01b0381351690602001356117e4565b6105c4611837565b6105c461183d565b61062a611843565b604080516001600160a01b039092168252519081900360200190f35b6105c4611852565b6105c4611858565b61065e61185e565b604080519115158252519081900360200190f35b6105c46004803603602081101561068857600080fd5b50356001600160a01b031661186e565b6105c4600480360360208110156106ae57600080fd5b50356001600160a01b0316611880565b6106dd600480360360208110156106d457600080fd5b50351515611892565b005b6106dd600480360360208110156106f557600080fd5b50356001600160a01b03166118b6565b6106dd6004803603604081101561071b57600080fd5b81359190810190604081016020820135600160201b81111561073c57600080fd5b82018360208201111561074e57600080fd5b803590602001918460018302840111600160201b8311171561076f57600080fd5b5090925090506118e7565b6105c46004803603602081101561079057600080fd5b50356001600160a01b0316611965565b6105c4600480360360408110156107b657600080fd5b506001600160a01b038135169060200135611979565b6105c4600480360360808110156107e257600080fd5b506001600160a01b0381358116916020810135821691604082013516906060013515156119be565b6106dd6004803603602081101561082057600080fd5b50351515611a14565b6105c4611a36565b61065e611a3c565b61065e611a4a565b6105c46004803603602081101561085757600080fd5b50356001600160a01b0316611a53565b6106dd600480360360e081101561087d57600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a081013515159060c001351515611b13565b6105c4611c55565b61065e611c5b565b6106dd60048036036101208110156108dc57600080fd5b5080359060208101359060408101359060608101359060808101359060a08101359060c08101359060e08101359061010001351515611c6a565b6105c46004803603606081101561092c57600080fd5b508035906001600160a01b0360208201358116916040013516611d24565b61065e6004803603602081101561096057600080fd5b50356001600160a01b0316611dc6565b6106dd6004803603604081101561098657600080fd5b506001600160a01b0381351690602001351515611ddb565b6109dc600480360360808110156109b457600080fd5b506001600160a01b038135811691602081013582169160408201351690606001351515611e0e565b60408051921515835260208301919091528051918290030190f35b6106dd600480360360a0811015610a0d57600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013590608001351515611eac565b61062a6122d9565b610a8160048036036080811015610a5957600080fd5b506001600160a01b0381358116916020810135821691604082013516906060013515156122e8565b604080519889526020890197909752878701959095526060870193909352608086019190915260a0850152151560c084015260e083015251908190036101000190f35b6105c460048036036020811015610ada57600080fd5b50356001600160a01b03166123de565b6105c46123f0565b6105c46123f6565b610b1760048036036020811015610b1057600080fd5b50356123fc565b604080519788526020880196909652868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b6105c460048036036080811015610b6557600080fd5b506001600160a01b038135811691602081013582169160408201351690606001351515612439565b6105c460048036036020811015610ba357600080fd5b50356001600160a01b03166124df565b61065e60048036036020811015610bc957600080fd5b50356001600160a01b03166124f1565b6105c460048036036020811015610bef57600080fd5b50356001600160a01b0316612506565b6109dc600480360360a0811015610c1557600080fd5b506001600160a01b0381351690602081013590604081013590606081013515159060800135612518565b6106dd60048036036020811015610c5557600080fd5b50356001600160a01b0316612610565b61065e60048036036040811015610c7b57600080fd5b506001600160a01b03813581169160200135166126f3565b6105c4612713565b6105c460048036036020811015610cb157600080fd5b50356001600160a01b0316612719565b61062a61272b565b6106dd60048036036020811015610cdf57600080fd5b50356001600160a01b0316612741565b6105c461276f565b6106dd60048036036020811015610d0d57600080fd5b50356001600160a01b0316612776565b6105c460048036036040811015610d3357600080fd5b506001600160a01b03813581169160200135166127aa565b6106dd60048036036020811015610d6157600080fd5b50356001600160a01b0316612a3e565b6106dd600480360360c0811015610d8757600080fd5b506001600160a01b03813581169160208101358216916040820135169060608101359060808101359060a00135612a68565b61062a612adf565b6105c4612aee565b6106dd60048036036020811015610ddf57600080fd5b50351515612af4565b6105c460048036036040811015610dfe57600080fd5b506001600160a01b0381358116916020013516612b18565b6105c460048036036020811015610e2c57600080fd5b50356001600160a01b0316612de8565b6105c4600480360360e0811015610e5257600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359160808201359160a081013515159160c09091013516612e93565b6106dd60048036036040811015610ea657600080fd5b506001600160a01b038135169060200135612f11565b6105c4612f35565b6105c460048036036020811015610eda57600080fd5b50356001600160a01b0316612f3a565b6106dd60048036036060811015610f0057600080fd5b5080359060208101359060400135612f4c565b6105c460048036036020811015610f2957600080fd5b50356001600160a01b0316612f92565b6105c460048036036020811015610f4f57600080fd5b50356001600160a01b0316612fa4565b6106dd60048036036020811015610f7557600080fd5b50356001600160a01b0316612fb6565b61065e612fe0565b6105c460048036036060811015610fa357600080fd5b506001600160a01b038135811691602081013582169160409091013516612fef565b6105c46132d7565b6105c460048036036020811015610fe357600080fd5b50356001600160a01b03166132e7565b6105c46132f9565b6105c46004803603604081101561101157600080fd5b506001600160a01b0381351690602001356132ff565b6105c46004803603606081101561103d57600080fd5b506001600160a01b038135169060208101359060400135613321565b61065e6133ca565b6105c46133d3565b6105c46004803603604081101561107f57600080fd5b506001600160a01b0381351690602001356133d9565b6106dd600480360360408110156110ab57600080fd5b506001600160a01b03813516906020013515156133f6565b6105c4600480360360208110156110d957600080fd5b50356001600160a01b0316613429565b61065e613527565b6105c46004803603602081101561110757600080fd5b50356001600160a01b0316613530565b6105c46004803603602081101561112d57600080fd5b50356001600160a01b0316613542565b6105c4613554565b61065e61355a565b6105c46004803603602081101561116357600080fd5b50356001600160a01b0316613568565b6105c46004803603606081101561118957600080fd5b506001600160a01b038135811691602081013590911690604001351515613609565b6109dc600480360360208110156111c157600080fd5b50356001600160a01b03166136a4565b6105c4600480360360208110156111e757600080fd5b50356001600160a01b031661373c565b6105c461374e565b6105c46004803603602081101561121557600080fd5b50356001600160a01b0316613754565b6105c4600480360360a081101561123b57600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001351515613766565b6106dd6004803603606081101561127b57600080fd5b506001600160a01b03813581169160208101359091169060400135613810565b6106dd600480360360208110156112b157600080fd5b50356001600160a01b0316613831565b6106dd600480360360208110156112d757600080fd5b503561385b565b6106dd600480360360208110156112f457600080fd5b5035613868565b611341600480360360a081101561131157600080fd5b506001600160a01b0381358116916020810135821691604082013516906060810135151590608001351515613884565b6040805192835260208301919091528051918290030190f35b6106dd6004803603604081101561137057600080fd5b506001600160a01b03813516906020013561393b565b6105c46004803603602081101561139c57600080fd5b50356001600160a01b0316613994565b6105c46139a6565b6105c4600480360360c08110156113ca57600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013515159060808101359060a001356139ac565b61065e6004803603602081101561141457600080fd5b50356001600160a01b0316613a60565b61065e6004803603602081101561143a57600080fd5b50356001600160a01b0316613a75565b6105c4600480360360e081101561146057600080fd5b506001600160a01b0381351690602081013590604081013590606081013515159060808101359060a08101359060c00135613a89565b6105c4613b1a565b6106dd600480360360a08110156114b457600080fd5b506001600160a01b03813581169160208101358216916040820135811691606081013515159160809091013516613b20565b6105c4613ee6565b6105c46004803603602081101561150457600080fd5b50356001600160a01b0316613eec565b61062a6004803603602081101561152a57600080fd5b5035613f64565b6106dd6004803603602081101561154757600080fd5b50356001600160a01b0316613f8b565b6106dd6004803603604081101561156d57600080fd5b506001600160a01b03813516906020013561405e565b6105c46004803603602081101561159957600080fd5b50356001600160a01b0316614082565b6106dd600480360360208110156115bf57600080fd5b50351515614094565b6105c4600480360360408110156115de57600080fd5b506001600160a01b03813581169160200135166140ba565b61065e6004803603602081101561160c57600080fd5b50356001600160a01b0316614110565b61062a614125565b61062a614134565b6105c46004803603606081101561164257600080fd5b506001600160a01b038135169060208101359060400135614143565b6106dd6004803603604081101561167457600080fd5b506001600160a01b038135811691602001351661417d565b6105c4614356565b6105c4600480360360a08110156116aa57600080fd5b506001600160a01b03813581169160208101358216916040820135169060608101351515906080013561435c565b6116f5600480360360208110156116ee57600080fd5b50356143d2565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561172f578181015183820152602001611717565b50505050905090810190601f16801561175c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6001600160a01b038116600090815260256020526040812054806117925760009150506117c8565b6001600160a01b0383166000908152602660205260409020546117c49082906117be90620f424061446d565b906144c6565b9150505b919050565b6904ee2d6d415b85acef8160201b81565b601b5490565b6000816117f357506000611831565b60006117fe84612de8565b6001600160a01b0385166000908152601d602052604090205490915061182c600a82900a6117be868561446d565b925050505b92915050565b600b5481565b61271081565b6006546001600160a01b031681565b60145481565b60095481565b6016546301000000900460ff1681565b60236020526000908152604090205481565b602c6020526000908152604090205481565b61189a614505565b60168054911515620100000262ff000019909216919091179055565b3360009081526018602090815260408083206001600160a01b0394909416835292905220805460ff19166001179055565b6002546001600160a01b03163314611946576040805162461bcd60e51b815260206004820152601e60248201527f5661756c743a20696e76616c6964206572726f72436f6e74726f6c6c65720000604482015290519081900360640190fd5b600083815260306020526040902061195f908383615d1c565b50505050565b60006118318261197484613568565b6117e4565b60008061198584613eec565b905060006119a3826117be8668327cb2734119d3b7a9601e1b61446d565b60055490915061182c9082906001600160a01b031687611d24565b604080516001600160601b0319606096871b811660208084019190915295871b811660348301529390951b9092166048850152151560f81b605c8401528051603d818503018152605d9093019052815191012090565b611a1c614505565b600180549115156101000261ff0019909216919091179055565b600f5481565b600154610100900460ff1681565b60015460ff1681565b600080600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611aa457600080fd5b505afa158015611ab8573d6000803e3d6000fd5b505050506040513d6020811015611ace57600080fd5b5051905080611ae15760009150506117c8565b6001600160a01b038316600090815260226020526040902054601554611b0b906117be838561446d565b949350505050565b611b1b614505565b6001600160a01b0387166000908152601c602052604090205460ff16611b9857600754611b49906001614520565b600755601b80546001810182556000919091527f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc10180546001600160a01b0319166001600160a01b0389161790555b6015546001600160a01b038816600090815260226020526040902054611bbf908290614578565b6001600160a01b0389166000908152601c602090815260408083208054600160ff1991821617909155601d83528184208c9055602283528184208b9055601e83528184208a905560248352818420899055601f83528184208054821689151517905591805290912080549091168415151790559050611c3e8187614520565b601555611c4a88613eec565b505050505050505050565b60175481565b60015462010000900460ff1681565b611c72614505565b611c826101f48a111560036145ba565b611c926101f489111560046145ba565b611ca26101f488111560056145ba565b611cb26101f487111560066145ba565b611cc26101f486111560076145ba565b611cd26101f485111560086145ba565b611ced6904ee2d6d415b85acef8160201b84111560096145ba565b600a98909855600b96909655600c94909455600d92909255600e55600f556009556010556011805460ff1916911515919091179055565b60055460009081906001600160a01b03858116911614611d5c576001600160a01b0384166000908152601d6020526040902054611d5f565b60125b6005549091506000906001600160a01b03858116911614611d98576001600160a01b0384166000908152601d6020526040902054611d9b565b60125b9050611dba82600a0a6117be83600a0a8961446d90919063ffffffff16565b925050505b9392505050565b601f6020526000908152604090205460ff1681565b611de3614505565b6001600160a01b03919091166000908152601960205260409020805460ff1916911515919091179055565b6000806000611e1f878787876119be565b9050611e29615d9a565b506000818152602b6020908152604091829020825160e081018452815480825260018301549382019390935260028201549381018490526003820154606082015260048201546080820152600582015460a082015260069091015460c082018190529092611e9d9289929091908990612518565b93509350505094509492505050565b60026000541415611ef2576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055600154611f0e9062010000900460ff16601c6145ba565b611f16614666565b611f1f85614682565b611f2a8484836146e5565b60015460408051634eae147d60e11b81526001600160a01b038881166004830152878116602483015286811660448301526064820186905284151560848301529151630100000090930490911691639d5c28fa9160a480820192600092909190829003018186803b158015611f9e57600080fd5b505afa158015611fb2573d6000803e3d6000fd5b50505050611fc0848461417d565b6000611fce868686856119be565b6000818152602b6020526040812091925083611ff257611fed86612de8565b611ffb565b611ffb86613eec565b825490915061200c57600282018190555b81541580159061201c5750600085115b156120425761203c868360000154846002015487858a8860060154613a89565b60028301555b600061205b898989888a886000015489600301546147fa565b90506000612068896148b6565b905060006120768a836117e4565b60018601549091506120889082614520565b6001860181905561209d90841115601d6145ba565b60018501546120ac9084614578565b60018601556120bc8a8a89613609565b600386015584546120cd9089614520565b8086554260068701556120e3901515601e6145ba565b6120f585600001548660010154614963565b6121038b8b8b8a6001613884565b505060006121118b8a6133d9565b60048701549091506121239082614520565b60048701556121328b82614987565b87156121775761214b8b6121468b87614520565b614a27565b6121558b83614aa6565b61215f8b84614b25565b6121728b61216d8d876132ff565b614c3d565b6121e3565b6001600160a01b038a166000908152602d60205260409020546121b4576001600160a01b038a166000908152602e602052604090208590556121d9565b6121bf8a868b613321565b6001600160a01b038b166000908152602e60205260409020555b6121e38a8a614d10565b604080518881526001600160a01b03808f166020830152808e16828401528c1660608201526080810184905260a081018b905289151560c082015260e08101879052610100810186905290517f2fe68525253654c21998f35787a8d0f361905ef647c854092430ab65f2f15022918190036101200190a1855460018701546002880154600389015460048a015460058b0154604080518e81526020810197909752868101959095526060860193909352608085019190915260a084015260c083015260e0820187905251600080516020615e0d833981519152918190036101000190a15050600160005550505050505050505050565b6002546001600160a01b031681565b60008060008060008060008060006123028d8d8d8d6119be565b905061230c615d9a565b602b60008381526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152505090506000808260a001511361238c578160a00151600003612392565b8160a001515b9050816000015182602001518360400151846060015185608001518560008860a0015112158860c001519a509a509a509a509a509a509a509a5050505094995094995094999196509450565b60276020526000908152604090205481565b6101f481565b600c5481565b602b602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b600080612448868686866119be565b9050612452615d9a565b506000818152602b6020908152604091829020825160e0810184528154815260018201549281018390526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c08301526124bc90151560256145ba565b602081015181516124d491906117be9061271061446d565b979650505050505050565b60216020526000908152604090205481565b60196020526000908152604090205460ff1681565b60256020526000908152604090205481565b6000806125296000861160266145ba565b60008461253e5761253988613eec565b612547565b61254788612de8565b905060008187116125615761255c8288614578565b61256b565b61256b8783614578565b9050600061257d886117be8b8561446d565b9050600087156125905750878311612595565b508288115b60006125ac6010548961452090919063ffffffff16565b42116125d0576001600160a01b038c166000908152601e60205260409020546125d3565b60005b90508180156125f657506125e78b8261446d565b6125f38461271061446d565b11155b1561260057600092505b509a909950975050505050505050565b60026000541415612656576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b600260009081556001600160a01b0382168152601c60205260409020546126819060ff16600e6145ba565b600061268c826148b6565b905061269c60008211600f6145ba565b6126a68282614b25565b604080516001600160a01b03841681526020810183905281517fa5a389190ebf6170a133bda5c769b77f4d6715b8aa172ec0ddf8473d0b4944bd929181900390910190a150506001600055565b601860209081526000928352604080842090915290825290205460ff1681565b60075481565b602e6020526000908152604090205481565b600154630100000090046001600160a01b031681565b3360009081526018602090815260408083206001600160a01b0394909416835292905220805460ff19169055565b620f424081565b61277e614505565b600180546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b6000600260005414156127f2576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b60026000556127ff614dc8565b6001600160a01b0383166000908152601c60205260409020546128269060ff1660136145ba565b6016805461ff00191661010017905560055460009061284d906001600160a01b03166148b6565b905061285d6000821160146145ba565b612867848561417d565b60006128738583611979565b90506128836000821160156145ba565b61288d8583614df7565b6128978582614c3d565b60055460408051632770a7eb60e21b81523060048201526024810185905290516001600160a01b0390921691639dc29fac9160448082019260009290919082900301818387803b1580156128ea57600080fd5b505af11580156128fe573d6000803e3d6000fd5b505060055461291892506001600160a01b03169050614eb5565b6001546040805163eb0835bf60e01b81526001600160a01b038881166004830152602482018690529151600093630100000090049092169163eb0835bf91604480820192602092909190829003018186803b15801561297657600080fd5b505afa15801561298a573d6000803e3d6000fd5b505050506040513d60208110156129a057600080fd5b5051905060006129b1878484614f50565b90506129c16000821160166145ba565b6129cc878288615018565b604080516001600160a01b03808916825289166020820152808201869052606081018390526080810184905290517fd732b7828fa6cee72c285eac756fc66a7477e3dc22e22e7c432f1c265d40b4839181900360a00190a16016805461ff001916905560016000559695505050505050565b612a46614505565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b612a70614505565b60018054612a849160ff90911615906145ba565b6001805460ff191681179055600380546001600160a01b03199081166001600160a01b039889161790915560058054821696881696909617909555600480549095169390951692909217909255600991909155601355601455565b6004546001600160a01b031681565b600a5481565b612afc614505565b60018054911515620100000262ff000019909216919091179055565b600060026000541415612b60576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055612b6d614dc8565b6001600160a01b0383166000908152601c6020526040902054612b949060ff1660106145ba565b6016805461ff0019166101001790556000612bae846148b6565b9050612bbe6000821160116145ba565b612bc8848561417d565b6000612bd385612de8565b90506000612bf168327cb2734119d3b7a9601e1b6117be858561446d565b600554909150612c0d90829088906001600160a01b0316611d24565b9050612c1d6000821160126145ba565b6001546040805163256f6ee360e11b81526001600160a01b0389811660048301526024820185905291516000936301000000900490921691634adeddc691604480820192602092909190829003018186803b158015612c7b57600080fd5b505afa158015612c8f573d6000803e3d6000fd5b505050506040513d6020811015612ca557600080fd5b505190506000612cb6888684614f50565b90506000612cd468327cb2734119d3b7a9601e1b6117be848861446d565b600554909150612cf09082908b906001600160a01b0316611d24565b9050612cfc89826150bf565b612d068983614b25565b600554604080516340c10f1960e01b81526001600160a01b038b8116600483015260248201859052915191909216916340c10f1991604480830192600092919082900301818387803b158015612d5b57600080fd5b505af1158015612d6f573d6000803e3d6000fd5b5050604080516001600160a01b03808d1682528d1660208201528082018a9052606081018590526080810187905290517fab4c77c74cd32c85f35416cf03e7ce9e2d4387f7b7f2c1f4bf53daaecf8ea72d93509081900360a0019150a16016805461ff0019169055600160005598975050505050505050565b60048054601654604080516317e1d38560e11b81526001600160a01b038681169582019590955260006024820181905260ff80851615156044840152610100909404909316151560648201529051919390921691632fc3a70a916084808301926020929190829003018186803b158015612e6157600080fd5b505afa158015612e75573d6000803e3d6000fd5b505050506040513d6020811015612e8b57600080fd5b505192915050565b600060026000541415612edb576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055612ee8614666565b612ef188614682565b612f008888888888888861517d565b600160005598975050505050505050565b612f19614505565b6001600160a01b03909116600090815260276020526040902055565b601281565b601e6020526000908152604090205481565b612f54614505565b612f64610e10841015600a6145ba565b612f74612710831115600b6145ba565b612f84612710821115600c6145ba565b601292909255601355601455565b602d6020526000908152604090205481565b601d6020526000908152604090205481565b612fbe614505565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60165462010000900460ff1681565b600060026000541415613037576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b600260005560015461305290610100900460ff1660176145ba565b6001600160a01b0384166000908152601c60205260409020546130799060ff1660186145ba565b6001600160a01b0383166000908152601c60205260409020546130a09060ff1660196145ba565b6130c0836001600160a01b0316856001600160a01b03161415601a6145ba565b6016805461ff0019166101001790556130d9848061417d565b6130e3838461417d565b60006130ee856148b6565b90506130fe60008211601b6145ba565b600061310986612de8565b9050600061311686613eec565b90506000613128826117be868661446d565b9050613135818989611d24565b9050600061315368327cb2734119d3b7a9601e1b6117be878761446d565b60055490915061316f9082908b906001600160a01b0316611d24565b60015460408051636d099c0b60e11b81526001600160a01b038d811660048301528c8116602483015260448201859052915193945060009363010000009093049091169163da13381691606480820192602092909190829003018186803b1580156131d957600080fd5b505afa1580156131ed573d6000803e3d6000fd5b505050506040513d602081101561320357600080fd5b5051905060006132148a8584614f50565b90506132208b846150bf565b61322a8a84614df7565b6132348b88614b25565b61323e8a85614c3d565b6132478a61560a565b6132528a828b615018565b604080516001600160a01b03808c168252808e1660208301528c1681830152606081018990526080810186905260a0810183905260c0810184905290517f0874b2d545cb271cdbda4e093020c452328b24af12382ed62c4d00f5c26709db9181900360e00190a16016805461ff001916905560016000559a9950505050505050505050565b68327cb2734119d3b7a9601e1b81565b602f6020526000908152604090205481565b60125481565b60008161330e57506000611831565b611dbf838361331c86613eec565b614143565b6001600160a01b0383166000908152602d6020908152604080832054602e9092528220548285821161335c576133578683614578565b613366565b6133668287614578565b90506000613378836117be868561446d565b905086831160006133898689614520565b90506000826133a15761339c8285614520565b6133ab565b6133ab8285614578565b90506133bb816117be8c8561446d565b9b9a5050505050505050505050565b60115460ff1681565b600d5481565b6000816133e857506000611831565b611dbf838361331c86612de8565b6133fe614505565b6001600160a01b03919091166000908152601a60205260409020805460ff1916911515919091179055565b6012546001600160a01b0382166000908152602a60205260408120549091429161345291614520565b1115613460575060006117c8565b6012546001600160a01b0383166000908152602a6020526040812054909161348d916117be904290614578565b6001600160a01b038416600090815260256020526040902054909150806134b9576000925050506117c8565b6001600160a01b0384166000908152601f602052604081205460ff166134e1576013546134e5565b6014545b6001600160a01b03861660009081526026602052604090205490915061351e9083906117be90869061351890869061446d565b9061446d565b95945050505050565b60165460ff1681565b60226020526000908152604090205481565b60246020526000908152604090205481565b60085481565b601654610100900460ff1681565b6001600160a01b0381166000908152601f602052604081205460ff16156135a857506001600160a01b0381166000908152602560205260409020546117c8565b6001600160a01b0382166000908152602860205260408120546135cc9084906132ff565b6001600160a01b0384166000908152602660209081526040808320546025909252909120549192506117c491613603908490614520565b90614578565b6001546040805163b1cc53ab60e01b81526001600160a01b038681166004830152858116602483015284151560448301529151600093630100000090049092169163b1cc53ab91606480820192602092909190829003018186803b15801561367057600080fd5b505afa158015613684573d6000803e3d6000fd5b505050506040513d602081101561369a57600080fd5b5051949350505050565b6001600160a01b0381166000908152602d60205260408120548190806136d1576000809250925050613737565b60006136dc85613eec565b6001600160a01b0386166000908152602e602052604081205491925082821161370e576137098383614578565b613718565b6137188284614578565b9050600061372a836117be878561446d565b9390921195509193505050505b915091565b60266020526000908152604090205481565b60135481565b60296020526000908152604090205481565b6001546040805163c7e074c360e01b81526001600160a01b03888116600483015260248201889052604482018790526064820186905284151560848301529151600093630100000090049092169163c7e074c39160a480820192602092909190829003018186803b1580156137da57600080fd5b505afa1580156137ee573d6000803e3d6000fd5b505050506040513d602081101561380457600080fd5b50519695505050505050565b613818614505565b61382c6001600160a01b038316848361567f565b505050565b613839614505565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b613863614505565b601755565b613870614505565b61387f612710821160026145ba565b600855565b6001546040805163d54d5a9f60e01b81526001600160a01b03888116600483015287811660248301528681166044830152851515606483015284151560848301528251600094859463010000009091049092169263d54d5a9f9260a4808301939192829003018186803b1580156138fa57600080fd5b505afa15801561390e573d6000803e3d6000fd5b505050506040513d604081101561392457600080fd5b508051602090910151909890975095505050505050565b613943614505565b6001600160a01b0382166000908152602360205260409020548082111561397d57613977836139728484614578565b6150bf565b50613990565b61382c8361398b8385614578565b614df7565b5050565b602a6020526000908152604090205481565b60105481565b6001546040805163369d949360e21b81526001600160a01b0389811660048301528881166024830152878116604483015286151560648301526084820186905260a482018590529151600093630100000090049092169163da76524c9160c480820192602092909190829003018186803b158015613a2957600080fd5b505afa158015613a3d573d6000803e3d6000fd5b505050506040513d6020811015613a5357600080fd5b5051979650505050505050565b601c6020526000908152604090205460ff1681565b602080526000908152604090205460ff1681565b6000806000613a9b8a8a8a8a88612518565b90925090506000613aac8a87614520565b905060008815613adb5783613aca57613ac58284614578565b613ad4565b613ad48284614520565b9050613afc565b83613aef57613aea8284614520565b613af9565b613af98284614578565b90505b613b0a816117be8a8561446d565b9c9b505050505050505050505050565b60155481565b60026000541415613b66576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b60026000556016546301000000900460ff1615613b9b5733600090815260196020526040902054613b9b9060ff1660226145ba565b6016805460ff19169055613baf848461417d565b6000613bbd868686866119be565b9050613bc7615d9a565b506000818152602b6020908152604091829020825160e08101845281548082526001830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c0830152613c3390151560236145ba565b600080613c44898989896000613884565b91509150613c57826000141560246145ba565b8160021415613c8c57613c74898989600087600001518b8f61517d565b50506016805460ff1916600117905550613eda915050565b6000613c9889836132ff565b6001600160a01b038a166000908152602c6020526040902054909150613cbe9082614520565b6001600160a01b038a166000818152602c60209081526040918290209390935580519182529181018490528082018390529051600080516020615eb88339815191529181900360600190a1613d178985608001516156d1565b8615613d465760208401518451613d38918b91613d3391614578565b614aa6565b613d468961216d8b856132ff565b600087613d5b57613d5689613eec565b613d64565b613d6489612de8565b90507f2e1f85a64a2f22cf2f0c42584e7c919ed4abe8d53675cff0f62bf1e95a1c676f868c8c8c8c8a600001518b602001518c608001518d60a001518a604051808b81526020018a6001600160a01b03168152602001896001600160a01b03168152602001886001600160a01b0316815260200187151581526020018681526020018581526020018481526020018381526020018281526020019a505050505050505050505060405180910390a187158015613e235750846020015183105b15613e51576020850151600090613e3a9085614578565b9050613e4f8b613e4a8d846132ff565b614b25565b505b87613e6457613e64898660000151615786565b6000868152602b60205260408120818155600181018290556002810182905560038101829055600481018290556005810182905560060155600954613eb0908b9061216d9082906132ff565b613ec68a613ec08c6009546132ff565b89615018565b50506016805460ff19166001179055505050505b50506001600055505050565b600e5481565b60048054601654604080516317e1d38560e11b81526001600160a01b03868116958201959095526001602482015260ff80841615156044830152610100909304909216151560648301525160009390921691632fc3a70a91608480820192602092909190829003018186803b158015612e6157600080fd5b601b8181548110613f7157fe5b6000918252602090912001546001600160a01b0316905081565b613f93614505565b6001600160a01b0381166000908152601c6020526040902054613fba9060ff16600d6145ba565b6001600160a01b038116600090815260226020526040902054601554613fdf91614578565b6015556001600160a01b0381166000908152601c60209081526040808320805460ff19908116909155601d835281842084905560228352818420849055601e835281842084905560248352818420849055601f835281842080548216905591805290912080549091169055600754614058906001614578565b60075550565b614066614505565b6001600160a01b039091166000908152602f6020526040902055565b60286020526000908152604090205481565b61409c614505565b6016805491151563010000000263ff00000019909216919091179055565b60006140c4614505565b6001600160a01b0383166000908152602c6020526040902054806140ec576000915050611831565b6001600160a01b0384166000908152602c6020526040812055611dbf848285615018565b601a6020526000908152604090205460ff1681565b6005546001600160a01b031681565b6003546001600160a01b031681565b60008261415257506000611dbf565b6001600160a01b0384166000908152601d602052604090205461351e836117be86600a85900a61446d565b6001546040805163fbfded6d60e01b81526001600160a01b03858116600483015284811660248301529151600093630100000090049092169163fbfded6d9160448082019260209290919082900301818787803b1580156141dd57600080fd5b505af11580156141f1573d6000803e3d6000fd5b505050506040513d602081101561420757600080fd5b50519050806142165750613990565b6001600160a01b0383166000908152602a6020526040902054614263576012546142449061351842826144c6565b6001600160a01b0384166000908152602a602052604090205550613990565b6012546001600160a01b0384166000908152602a6020526040902054429161428b9190614520565b11156142975750613990565b60006142a284613429565b6001600160a01b0385166000908152602960205260409020549091506142c89082614520565b6001600160a01b0385166000908152602960205260409020556012546142f29061351842826144c6565b6001600160a01b0385166000818152602a602090815260408083209490945560298152908390205483519283529082015281517fa146fc154e1913322e9817d49f0d5c37466c24326e15de10e739a948be815eab929181900390910190a150505050565b610e1081565b6001546040805163fdaf6ac360e01b81526001600160a01b038881166004830152878116602483015286811660448301528515156064830152608482018590529151600093630100000090049092169163fdaf6ac39160a480820192602092909190829003018186803b1580156137da57600080fd5b60306020908152600091825260409182902080548351601f6002600019610100600186161502019093169290920491820184900484028101840190945280845290918301828280156144655780601f1061443a57610100808354040283529160200191614465565b820191906000526020600020905b81548152906001019060200180831161444857829003601f168201915b505050505081565b60008261447c57506000611831565b8282028284828161448957fe5b0414611dbf5760405162461bcd60e51b8152600401808060200182810382526021815260200180615e4d6021913960400191505060405180910390fd5b6000611dbf83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b8152506157ee565b60065461451e906001600160a01b0316331460356145ba565b565b600082820183811015611dbf576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6000611dbf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250615890565b60008181526030602052604090208261382c5760405162461bcd60e51b81526020600482019081528254600260001961010060018416150201909116046024830181905290918291604490910190849080156146575780601f1061462c57610100808354040283529160200191614657565b820191906000526020600020905b81548152906001019060200180831161463a57829003601f168201915b50509250505060405180910390fd5b6017546146725761451e565b61451e6017543a111560376145ba565b336001600160a01b0382161415614698576146e2565b6003546001600160a01b03163314156146b0576146e2565b6001600160a01b03811660009081526018602090815260408083203384529091529020546146e29060ff1660296145ba565b50565b801561475e5761470a826001600160a01b0316846001600160a01b031614602a6145ba565b6001600160a01b0383166000908152601c60205260409020546147319060ff16602b6145ba565b6001600160a01b0383166000908152601f60205260409020546147599060ff1615602c6145ba565b61382c565b6001600160a01b0383166000908152601c60205260409020546147859060ff16602d6145ba565b6001600160a01b0383166000908152601f60205260409020546147ac9060ff16602e6145ba565b6001600160a01b0382166000908152601f60205260409020546147d49060ff1615602f6145ba565b6001600160a01b038216600090815260208052604090205461382c9060ff1660306145ba565b60008061480a898989898961435c565b9050600061481c8a8a8a8a89896139ac565b90506148288282614520565b915060006148368a846132ff565b6001600160a01b038b166000908152602c602052604090205490915061485c9082614520565b6001600160a01b038b166000818152602c60209081526040918290209390935580519182529181018590528082018390529051600080516020615eb88339815191529181900360600190a150909998505050505050505050565b6001600160a01b03811660008181526021602090815260408083205481516370a0823160e01b8152306004820152915193949093859391926370a08231926024808301939192829003018186803b15801561491057600080fd5b505afa158015614924573d6000803e3d6000fd5b505050506040513d602081101561493a57600080fd5b50516001600160a01b03851660009081526021602052604090208190559050611b0b8183614578565b8161497957614974811560276145ba565b613990565b6139908183101560286145ba565b6001600160a01b0382166000908152602660205260409020546149aa9082614520565b6001600160a01b0383166000908152602660208181526040808420859055602582529092205491526149df91111560346145ba565b604080516001600160a01b03841681526020810183905281517faa5649d82f5462be9d19b0f2b31a59b2259950a6076550bac9f3a1c07db9f66d929181900390910190a15050565b6001600160a01b038216600090815260286020526040902054614a4a9082614520565b6001600160a01b03831660008181526028602090815260409182902093909355805191825291810183905281517fd9d4761f75e0d0103b5cbeab941eeb443d7a56a35b5baf2a0787c03f03f4e474929181900390910190a15050565b6001600160a01b038216600090815260286020526040902054614ac99082614578565b6001600160a01b03831660008181526028602090815260409182902093909355805191825291810183905281517f34e07158b9db50df5613e591c44ea2ebc82834eff4a4dc3a46e000e608261d68929181900390910190a15050565b6001600160a01b038216600090815260256020526040902054614b489082614520565b6001600160a01b03831660008181526025602090815260408083209490945583516370a0823160e01b8152306004820152935191936370a082319260248083019392829003018186803b158015614b9e57600080fd5b505afa158015614bb2573d6000803e3d6000fd5b505050506040513d6020811015614bc857600080fd5b50516001600160a01b038416600090815260256020526040902054909150614bf49082101560316145ba565b604080516001600160a01b03851681526020810184905281517f976177fbe09a15e5e43f848844963a42b41ef919ef17ff21a17a5421de8f4737929181900390910190a1505050565b604080518082018252601a81527915985d5b1d0e881c1bdbdb105b5bdd5b9d08195e18d95959195960321b6020808301919091526001600160a01b038516600090815260259091529190912054614c95918390615890565b6001600160a01b03831660009081526025602090815260408083208490556026909152902054614cc891101560326145ba565b604080516001600160a01b03841681526020810183905281517f112726233fbeaeed0f5b1dba5cb0b2b81883dee49fb35ff99fd98ed9f6d31eb0929181900390910190a15050565b6001600160a01b0382166000908152602d6020526040902054614d339082614520565b6001600160a01b0383166000908152602d6020908152604080832093909355602f90522054801561382c576001600160a01b0383166000908152602d602052604090205481101561382c576040805162461bcd60e51b815260206004820152601a60248201527915985d5b1d0e881b585e081cda1bdc9d1cc8195e18d95959195960321b604482015290519081900360640190fd5b60165462010000900460ff161561451e57336000908152601a602052604090205461451e9060ff1660366145ba565b6001600160a01b038216600090815260236020526040902054818111614e60576001600160a01b038316600081815260236020908152604080832092909255815192835282018390528051600080516020615e6e8339815191529281900390910190a150613990565b614e6a8183614578565b6001600160a01b0384166000818152602360209081526040918290209390935580519182529181018490528151600080516020615e6e833981519152929181900390910190a1505050565b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614f0457600080fd5b505afa158015614f18573d6000803e3d6000fd5b505050506040513d6020811015614f2e57600080fd5b50516001600160a01b0390921660009081526021602052604090209190915550565b600080614f6d6127106117be614f668287614578565b879061446d565b90506000614f7b8583614578565b6001600160a01b0387166000908152602c6020526040902054909150614fa19082614520565b6001600160a01b0387166000908152602c60205260409020557f47cd9dda0e50ce30bcaaacd0488452b596221c07ac402a581cfae4d3933cac2b86614fe681846117e4565b604080516001600160a01b0390931683526020830191909152818101849052519081900360600190a150949350505050565b61502c6001600160a01b038416828461567f565b604080516370a0823160e01b815230600482015290516001600160a01b038516916370a08231916024808301926020929190829003018186803b15801561507257600080fd5b505afa158015615086573d6000803e3d6000fd5b505050506040513d602081101561509c57600080fd5b50516001600160a01b039093166000908152602160205260409020929092555050565b6001600160a01b0382166000908152602360205260409020546150e29082614520565b6001600160a01b0383166000908152602360209081526040808320939093556024905220548015615134576001600160a01b0383166000908152602360205260409020546151349082101560336145ba565b604080516001600160a01b03851681526020810184905281517f64243679a443432e2293343b77d411ff6144370404618f00ca0d2025d9ca9882929181900390910190a1505050565b600154604080516381d11a2360e01b81526001600160a01b038a8116600483015289811660248301528881166044830152606482018890526084820187905285151560a483015284811660c4830152915160009363010000009004909216916381d11a239160e4808201928692909190829003018186803b15801561520157600080fd5b505afa158015615215573d6000803e3d6000fd5b50505050615223878761417d565b6000615231898989876119be565b6000818152602b60205260409020805491925090615252901515601f6145ba565b615264868260000154101560206145ba565b615276878260010154101560216145ba565b600181015481546004830154600091615293916117be908b61446d565b60048401549091506152a59082614578565b60048401556152b48b826156d1565b506000806152c68d8d8d8d8d8d6158ea565b855491935091508914615446576152de8c8c8a613609565b600385015583546152ef908a614578565b80855560018501546153019190614963565b61530f8d8d8d8b6001613884565b5050871561533c576153328c61214686600101548661457890919063ffffffff16565b61533c8c8a614aa6565b6000886153515761534c8c613eec565b61535a565b61535a8c612de8565b9050600080516020615e2d833981519152868f8f8f8f8f8f8861537d8c8c614578565b60408051998a526001600160a01b0398891660208b015296881689880152949096166060880152608087019290925260a0860152151560c085015260e084019290925261010083019190915251908190036101200190a18454600186015460028701546003880154600489015460058a0154604080518d81526020810197909752868101959095526060860193909352608085019190915260a084015260c083015260e0820183905251600080516020615e0d833981519152918190036101000190a1506155a8565b8715615460576154568c84614a27565b6154608c8a614aa6565b600088615475576154708c613eec565b61547e565b61547e8c612de8565b9050600080516020615e2d833981519152868f8f8f8f8f8f886154a18c8c614578565b60408051998a526001600160a01b0398891660208b015296881689880152949096166060880152608087019290925260a0860152151560c085015260e084019290925261010083019190915251908190036101200190a18454600186015460028701546003880154600489015460058a0154604080518d81526020810197909752868101959095526060860193909352608085019190915260a084015260c0830152517f73af1d417d82c240fdb6d319b34ad884487c6bf2845d98980cc52ad9171cb4559181900360e00190a1506000858152602b602052604081208181556001810182905560028101829055600381018290556004810182905560058101829055600601555b876155b7576155b78b8a615786565b81156155f75787156155d1576155d18c61216d8e856132ff565b60006155dd8d836132ff565b90506155ea8d828a615018565b95506124d4945050505050565b5060009c9b505050505050505050505050565b6001600160a01b03811660009081526027602090815260408083205460259092529091205410156146e2576040805162461bcd60e51b815260206004820152601a6024820152792b30bab63a1d103837b7b620b6b7bab73a101e10313ab33332b960311b604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261382c908490615ae4565b604080518082018252601b81527a5661756c743a20696e73756666696369656e74207265736572766560281b6020808301919091526001600160a01b03851660009081526026909152919091205461572a918390615890565b6001600160a01b03831660008181526026602090815260409182902093909355805191825291810183905281517f533cb5ed32be6a90284e96b5747a1bfc2d38fdb5768a6b5f67ff7d62144ed67b929181900390910190a15050565b6001600160a01b0382166000908152602d6020526040902054808211156157c657506001600160a01b0382166000908152602d6020526040812055613990565b6157d08183614578565b6001600160a01b0384166000908152602d6020526040902055505050565b6000818361587a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561583f578181015183820152602001615827565b50505050905090810190601f16801561586c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161588657fe5b0495945050505050565b600081848411156158e25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561583f578181015183820152602001615827565b505050900390565b60008060006158fb898989876119be565b6000818152602b60205260408120805460038201549394509092615928918d918d918d918b918d916147fa565b90506000806000806159498d876000015488600201548d8a60060154612518565b87549195508593509150615961906117be8d8461446d565b9250505060008280156159745750600082115b156159a457506005840180548201905580886159a45760006159968e846132ff565b90506159a28e82614c3d565b505b821580156159b25750600082115b156159f65760018501546159c69083614578565b6001860155886159ea5760006159dc8e846132ff565b90506159e88e82614b25565b505b60058501805483900390555b8a15615a1e57615a06818c614520565b6001860154909150615a18908c614578565b60018601555b84548a1415615a41576001850154615a37908290614520565b6000600187015590505b8084811115615a5b57615a548286614578565b9050615a8f565b6001860154615a6a9086614578565b60018701558915615a8f576000615a818f876132ff565b9050615a8d8f82614c3d565b505b60408051888152851515602082015280820185905290517f3ff41bdde87755b687ae83d0221a232b6be51a803330ed9661c1b5d0105e0d8a9181900360600190a1909e909d509b505050505050505050505050565b6060615b39826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316615b959092919063ffffffff16565b80519091501561382c57808060200190516020811015615b5857600080fd5b505161382c5760405162461bcd60e51b815260040180806020018281038252602a815260200180615e8e602a913960400191505060405180910390fd5b6060611b0b848460008585615ba985615cb0565b615bfa576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310615c395780518252601f199092019160209182019101615c1a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114615c9b576040519150601f19603f3d011682016040523d82523d6000602084013e615ca0565b606091505b50915091506124d4828286615cb6565b3b151590565b60608315615cc5575081611dbf565b825115615cd55782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561583f578181015183820152602001615827565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615d5d5782800160ff19823516178555615d8a565b82800160010185558215615d8a579182015b82811115615d8a578235825591602001919060010190615d6f565b50615d96929150615dd7565b5090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5b80821115615d965760008155600101615dd856fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c0020853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b77393d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77e1e812596aac93a06ecc4ca627014d18e30f5c33b825160cc9d5c0ba61e452275361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011aa2646970667358221220127f467dd7997c47df98a559f890d614307e268cbd07feca4f7c83135a70dc4664736f6c634300060c0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106105995760003560e01c80638585f4d2116102e9578063c7e074c31161018f578063c7e074c314611225578063cea0c32814611265578063cfad57a21461129b578063d2fa635e146112c1578063d3127e63146112de578063d54d5a9f146112fb578063d66b000d1461135a578063d8f897c314611386578063d9ac4225146113ac578063da76524c146113b4578063daf9c210146113fe578063db3555fb14611424578063db97495f1461144a578063dc8f5fac14611496578063de2ea9481461149e578063df73a267146114e6578063e124e6d2146114ee578063e468baf014611514578063e67f59a714611531578063efa10a6e14611557578063f07456ce14611583578063f07bbf77146115a9578063f2555278146115c8578063f3ae2415146115f6578063f5b91b7b1461161c578063f887ea4014611624578063fa12dbc01461162c578063fbfded6d1461165e578063fce28c101461168c578063fdaf6ac314611694578063fed1a606146116d857610599565b80638585f4d214610e90578063870d917c14610ebc57806388b1fbdf14610ec45780638a27d46814610eea5780638a39735a1461061a5780638a78daa814610f135780638ee573ac14610f395780638f7b840414610f5f5780639060b1ca14610f855780639331621214610f8d57806395082d2514610fc55780639698d25a14610fcd5780639849e41214610ff35780639899cd0214610ffb5780639d7432ca146110275780639f392eb314611059578063a22f239214611061578063a42ab3d214611069578063a5e90eee14611095578063a93acac2146110c3578063ab08c1c6146110e9578063ab2f3ad4146110f1578063ad1e4f8d14611117578063ae3302c21461113d578063b06423f314611145578063b136ca491461114d578063b1cc53ab14611173578063b364accb146111ab578063c3c7b9e9146111d1578063c4f718bf146111f7578063c65bc7b1146111ff57610599565b80634453a3741161044e5780634453a3741461097057806345a6f3701461099e57806348d91abf146109f757806348f35cbb14610a3b5780634a3f088d14610a435780634a993ee914610ac45780634befe2ca14610aea5780634d47b30414610af2578063514ea4bf14610afa57806351723e8214610b4f578063523fba7f14610b8d578063529a356f14610bb357806352f55eed14610bd95780635c07eaab14610bff5780635f7bc11914610c3f5780636092219914610c6557806362287a3214610c935780636274980314610c9b5780636abbe0c814610cc15780636ae0b15414610cc95780636be6026b14610cef57806371089f4d14610cf7578063711e619014610d1d578063724e78da14610d4b578063728cdbca14610d71578063741bef1a14610db95780637a210a2b14610dc15780637c2eb9f714610dc9578063817bb85714610de857806381a612d614610e1657806382a0849014610e3c57610599565b806304fef1db1461059e57806307c58752146105d65780630842b076146105de5780630a48d5a9146105e657806310eb56c214610612578063126082cf1461061a57806312d43a5114610622578063134ca63b14610646578063174d26941461064e578063181e210e146106565780631aa4ace5146106725780631ce9cb8f1461069857806324b0c04d146106be57806324ca984e146106df57806328e67be51461070557806329ff96151461077a5780632c668ec1146107a05780632d4b0576146107cc57806330455ede1461080a578063318bc6891461082957806334c1557d1461061a578063351a964d14610831578063392e53cd146108395780633a05dcc1146108415780633c5a6e35146108675780633de39c11146108b55780633e72a262146108bd57806340eb3802146108c5578063421528731461091657806342b60b031461094a575b600080fd5b6105c4600480360360208110156105b457600080fd5b50356001600160a01b031661176a565b60408051918252519081900360200190f35b6105c46117cd565b6105c46117de565b6105c4600480360360408110156105fc57600080fd5b506001600160a01b0381351690602001356117e4565b6105c4611837565b6105c461183d565b61062a611843565b604080516001600160a01b039092168252519081900360200190f35b6105c4611852565b6105c4611858565b61065e61185e565b604080519115158252519081900360200190f35b6105c46004803603602081101561068857600080fd5b50356001600160a01b031661186e565b6105c4600480360360208110156106ae57600080fd5b50356001600160a01b0316611880565b6106dd600480360360208110156106d457600080fd5b50351515611892565b005b6106dd600480360360208110156106f557600080fd5b50356001600160a01b03166118b6565b6106dd6004803603604081101561071b57600080fd5b81359190810190604081016020820135600160201b81111561073c57600080fd5b82018360208201111561074e57600080fd5b803590602001918460018302840111600160201b8311171561076f57600080fd5b5090925090506118e7565b6105c46004803603602081101561079057600080fd5b50356001600160a01b0316611965565b6105c4600480360360408110156107b657600080fd5b506001600160a01b038135169060200135611979565b6105c4600480360360808110156107e257600080fd5b506001600160a01b0381358116916020810135821691604082013516906060013515156119be565b6106dd6004803603602081101561082057600080fd5b50351515611a14565b6105c4611a36565b61065e611a3c565b61065e611a4a565b6105c46004803603602081101561085757600080fd5b50356001600160a01b0316611a53565b6106dd600480360360e081101561087d57600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a081013515159060c001351515611b13565b6105c4611c55565b61065e611c5b565b6106dd60048036036101208110156108dc57600080fd5b5080359060208101359060408101359060608101359060808101359060a08101359060c08101359060e08101359061010001351515611c6a565b6105c46004803603606081101561092c57600080fd5b508035906001600160a01b0360208201358116916040013516611d24565b61065e6004803603602081101561096057600080fd5b50356001600160a01b0316611dc6565b6106dd6004803603604081101561098657600080fd5b506001600160a01b0381351690602001351515611ddb565b6109dc600480360360808110156109b457600080fd5b506001600160a01b038135811691602081013582169160408201351690606001351515611e0e565b60408051921515835260208301919091528051918290030190f35b6106dd600480360360a0811015610a0d57600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013590608001351515611eac565b61062a6122d9565b610a8160048036036080811015610a5957600080fd5b506001600160a01b0381358116916020810135821691604082013516906060013515156122e8565b604080519889526020890197909752878701959095526060870193909352608086019190915260a0850152151560c084015260e083015251908190036101000190f35b6105c460048036036020811015610ada57600080fd5b50356001600160a01b03166123de565b6105c46123f0565b6105c46123f6565b610b1760048036036020811015610b1057600080fd5b50356123fc565b604080519788526020880196909652868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b6105c460048036036080811015610b6557600080fd5b506001600160a01b038135811691602081013582169160408201351690606001351515612439565b6105c460048036036020811015610ba357600080fd5b50356001600160a01b03166124df565b61065e60048036036020811015610bc957600080fd5b50356001600160a01b03166124f1565b6105c460048036036020811015610bef57600080fd5b50356001600160a01b0316612506565b6109dc600480360360a0811015610c1557600080fd5b506001600160a01b0381351690602081013590604081013590606081013515159060800135612518565b6106dd60048036036020811015610c5557600080fd5b50356001600160a01b0316612610565b61065e60048036036040811015610c7b57600080fd5b506001600160a01b03813581169160200135166126f3565b6105c4612713565b6105c460048036036020811015610cb157600080fd5b50356001600160a01b0316612719565b61062a61272b565b6106dd60048036036020811015610cdf57600080fd5b50356001600160a01b0316612741565b6105c461276f565b6106dd60048036036020811015610d0d57600080fd5b50356001600160a01b0316612776565b6105c460048036036040811015610d3357600080fd5b506001600160a01b03813581169160200135166127aa565b6106dd60048036036020811015610d6157600080fd5b50356001600160a01b0316612a3e565b6106dd600480360360c0811015610d8757600080fd5b506001600160a01b03813581169160208101358216916040820135169060608101359060808101359060a00135612a68565b61062a612adf565b6105c4612aee565b6106dd60048036036020811015610ddf57600080fd5b50351515612af4565b6105c460048036036040811015610dfe57600080fd5b506001600160a01b0381358116916020013516612b18565b6105c460048036036020811015610e2c57600080fd5b50356001600160a01b0316612de8565b6105c4600480360360e0811015610e5257600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359160808201359160a081013515159160c09091013516612e93565b6106dd60048036036040811015610ea657600080fd5b506001600160a01b038135169060200135612f11565b6105c4612f35565b6105c460048036036020811015610eda57600080fd5b50356001600160a01b0316612f3a565b6106dd60048036036060811015610f0057600080fd5b5080359060208101359060400135612f4c565b6105c460048036036020811015610f2957600080fd5b50356001600160a01b0316612f92565b6105c460048036036020811015610f4f57600080fd5b50356001600160a01b0316612fa4565b6106dd60048036036020811015610f7557600080fd5b50356001600160a01b0316612fb6565b61065e612fe0565b6105c460048036036060811015610fa357600080fd5b506001600160a01b038135811691602081013582169160409091013516612fef565b6105c46132d7565b6105c460048036036020811015610fe357600080fd5b50356001600160a01b03166132e7565b6105c46132f9565b6105c46004803603604081101561101157600080fd5b506001600160a01b0381351690602001356132ff565b6105c46004803603606081101561103d57600080fd5b506001600160a01b038135169060208101359060400135613321565b61065e6133ca565b6105c46133d3565b6105c46004803603604081101561107f57600080fd5b506001600160a01b0381351690602001356133d9565b6106dd600480360360408110156110ab57600080fd5b506001600160a01b03813516906020013515156133f6565b6105c4600480360360208110156110d957600080fd5b50356001600160a01b0316613429565b61065e613527565b6105c46004803603602081101561110757600080fd5b50356001600160a01b0316613530565b6105c46004803603602081101561112d57600080fd5b50356001600160a01b0316613542565b6105c4613554565b61065e61355a565b6105c46004803603602081101561116357600080fd5b50356001600160a01b0316613568565b6105c46004803603606081101561118957600080fd5b506001600160a01b038135811691602081013590911690604001351515613609565b6109dc600480360360208110156111c157600080fd5b50356001600160a01b03166136a4565b6105c4600480360360208110156111e757600080fd5b50356001600160a01b031661373c565b6105c461374e565b6105c46004803603602081101561121557600080fd5b50356001600160a01b0316613754565b6105c4600480360360a081101561123b57600080fd5b506001600160a01b0381351690602081013590604081013590606081013590608001351515613766565b6106dd6004803603606081101561127b57600080fd5b506001600160a01b03813581169160208101359091169060400135613810565b6106dd600480360360208110156112b157600080fd5b50356001600160a01b0316613831565b6106dd600480360360208110156112d757600080fd5b503561385b565b6106dd600480360360208110156112f457600080fd5b5035613868565b611341600480360360a081101561131157600080fd5b506001600160a01b0381358116916020810135821691604082013516906060810135151590608001351515613884565b6040805192835260208301919091528051918290030190f35b6106dd6004803603604081101561137057600080fd5b506001600160a01b03813516906020013561393b565b6105c46004803603602081101561139c57600080fd5b50356001600160a01b0316613994565b6105c46139a6565b6105c4600480360360c08110156113ca57600080fd5b506001600160a01b038135811691602081013582169160408201351690606081013515159060808101359060a001356139ac565b61065e6004803603602081101561141457600080fd5b50356001600160a01b0316613a60565b61065e6004803603602081101561143a57600080fd5b50356001600160a01b0316613a75565b6105c4600480360360e081101561146057600080fd5b506001600160a01b0381351690602081013590604081013590606081013515159060808101359060a08101359060c00135613a89565b6105c4613b1a565b6106dd600480360360a08110156114b457600080fd5b506001600160a01b03813581169160208101358216916040820135811691606081013515159160809091013516613b20565b6105c4613ee6565b6105c46004803603602081101561150457600080fd5b50356001600160a01b0316613eec565b61062a6004803603602081101561152a57600080fd5b5035613f64565b6106dd6004803603602081101561154757600080fd5b50356001600160a01b0316613f8b565b6106dd6004803603604081101561156d57600080fd5b506001600160a01b03813516906020013561405e565b6105c46004803603602081101561159957600080fd5b50356001600160a01b0316614082565b6106dd600480360360208110156115bf57600080fd5b50351515614094565b6105c4600480360360408110156115de57600080fd5b506001600160a01b03813581169160200135166140ba565b61065e6004803603602081101561160c57600080fd5b50356001600160a01b0316614110565b61062a614125565b61062a614134565b6105c46004803603606081101561164257600080fd5b506001600160a01b038135169060208101359060400135614143565b6106dd6004803603604081101561167457600080fd5b506001600160a01b038135811691602001351661417d565b6105c4614356565b6105c4600480360360a08110156116aa57600080fd5b506001600160a01b03813581169160208101358216916040820135169060608101351515906080013561435c565b6116f5600480360360208110156116ee57600080fd5b50356143d2565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561172f578181015183820152602001611717565b50505050905090810190601f16801561175c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6001600160a01b038116600090815260256020526040812054806117925760009150506117c8565b6001600160a01b0383166000908152602660205260409020546117c49082906117be90620f424061446d565b906144c6565b9150505b919050565b6904ee2d6d415b85acef8160201b81565b601b5490565b6000816117f357506000611831565b60006117fe84612de8565b6001600160a01b0385166000908152601d602052604090205490915061182c600a82900a6117be868561446d565b925050505b92915050565b600b5481565b61271081565b6006546001600160a01b031681565b60145481565b60095481565b6016546301000000900460ff1681565b60236020526000908152604090205481565b602c6020526000908152604090205481565b61189a614505565b60168054911515620100000262ff000019909216919091179055565b3360009081526018602090815260408083206001600160a01b0394909416835292905220805460ff19166001179055565b6002546001600160a01b03163314611946576040805162461bcd60e51b815260206004820152601e60248201527f5661756c743a20696e76616c6964206572726f72436f6e74726f6c6c65720000604482015290519081900360640190fd5b600083815260306020526040902061195f908383615d1c565b50505050565b60006118318261197484613568565b6117e4565b60008061198584613eec565b905060006119a3826117be8668327cb2734119d3b7a9601e1b61446d565b60055490915061182c9082906001600160a01b031687611d24565b604080516001600160601b0319606096871b811660208084019190915295871b811660348301529390951b9092166048850152151560f81b605c8401528051603d818503018152605d9093019052815191012090565b611a1c614505565b600180549115156101000261ff0019909216919091179055565b600f5481565b600154610100900460ff1681565b60015460ff1681565b600080600560009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611aa457600080fd5b505afa158015611ab8573d6000803e3d6000fd5b505050506040513d6020811015611ace57600080fd5b5051905080611ae15760009150506117c8565b6001600160a01b038316600090815260226020526040902054601554611b0b906117be838561446d565b949350505050565b611b1b614505565b6001600160a01b0387166000908152601c602052604090205460ff16611b9857600754611b49906001614520565b600755601b80546001810182556000919091527f3ad8aa4f87544323a9d1e5dd902f40c356527a7955687113db5f9a85ad579dc10180546001600160a01b0319166001600160a01b0389161790555b6015546001600160a01b038816600090815260226020526040902054611bbf908290614578565b6001600160a01b0389166000908152601c602090815260408083208054600160ff1991821617909155601d83528184208c9055602283528184208b9055601e83528184208a905560248352818420899055601f83528184208054821689151517905591805290912080549091168415151790559050611c3e8187614520565b601555611c4a88613eec565b505050505050505050565b60175481565b60015462010000900460ff1681565b611c72614505565b611c826101f48a111560036145ba565b611c926101f489111560046145ba565b611ca26101f488111560056145ba565b611cb26101f487111560066145ba565b611cc26101f486111560076145ba565b611cd26101f485111560086145ba565b611ced6904ee2d6d415b85acef8160201b84111560096145ba565b600a98909855600b96909655600c94909455600d92909255600e55600f556009556010556011805460ff1916911515919091179055565b60055460009081906001600160a01b03858116911614611d5c576001600160a01b0384166000908152601d6020526040902054611d5f565b60125b6005549091506000906001600160a01b03858116911614611d98576001600160a01b0384166000908152601d6020526040902054611d9b565b60125b9050611dba82600a0a6117be83600a0a8961446d90919063ffffffff16565b925050505b9392505050565b601f6020526000908152604090205460ff1681565b611de3614505565b6001600160a01b03919091166000908152601960205260409020805460ff1916911515919091179055565b6000806000611e1f878787876119be565b9050611e29615d9a565b506000818152602b6020908152604091829020825160e081018452815480825260018301549382019390935260028201549381018490526003820154606082015260048201546080820152600582015460a082015260069091015460c082018190529092611e9d9289929091908990612518565b93509350505094509492505050565b60026000541415611ef2576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055600154611f0e9062010000900460ff16601c6145ba565b611f16614666565b611f1f85614682565b611f2a8484836146e5565b60015460408051634eae147d60e11b81526001600160a01b038881166004830152878116602483015286811660448301526064820186905284151560848301529151630100000090930490911691639d5c28fa9160a480820192600092909190829003018186803b158015611f9e57600080fd5b505afa158015611fb2573d6000803e3d6000fd5b50505050611fc0848461417d565b6000611fce868686856119be565b6000818152602b6020526040812091925083611ff257611fed86612de8565b611ffb565b611ffb86613eec565b825490915061200c57600282018190555b81541580159061201c5750600085115b156120425761203c868360000154846002015487858a8860060154613a89565b60028301555b600061205b898989888a886000015489600301546147fa565b90506000612068896148b6565b905060006120768a836117e4565b60018601549091506120889082614520565b6001860181905561209d90841115601d6145ba565b60018501546120ac9084614578565b60018601556120bc8a8a89613609565b600386015584546120cd9089614520565b8086554260068701556120e3901515601e6145ba565b6120f585600001548660010154614963565b6121038b8b8b8a6001613884565b505060006121118b8a6133d9565b60048701549091506121239082614520565b60048701556121328b82614987565b87156121775761214b8b6121468b87614520565b614a27565b6121558b83614aa6565b61215f8b84614b25565b6121728b61216d8d876132ff565b614c3d565b6121e3565b6001600160a01b038a166000908152602d60205260409020546121b4576001600160a01b038a166000908152602e602052604090208590556121d9565b6121bf8a868b613321565b6001600160a01b038b166000908152602e60205260409020555b6121e38a8a614d10565b604080518881526001600160a01b03808f166020830152808e16828401528c1660608201526080810184905260a081018b905289151560c082015260e08101879052610100810186905290517f2fe68525253654c21998f35787a8d0f361905ef647c854092430ab65f2f15022918190036101200190a1855460018701546002880154600389015460048a015460058b0154604080518e81526020810197909752868101959095526060860193909352608085019190915260a084015260c083015260e0820187905251600080516020615e0d833981519152918190036101000190a15050600160005550505050505050505050565b6002546001600160a01b031681565b60008060008060008060008060006123028d8d8d8d6119be565b905061230c615d9a565b602b60008381526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815260200160068201548152505090506000808260a001511361238c578160a00151600003612392565b8160a001515b9050816000015182602001518360400151846060015185608001518560008860a0015112158860c001519a509a509a509a509a509a509a509a5050505094995094995094999196509450565b60276020526000908152604090205481565b6101f481565b600c5481565b602b602052600090815260409020805460018201546002830154600384015460048501546005860154600690960154949593949293919290919087565b600080612448868686866119be565b9050612452615d9a565b506000818152602b6020908152604091829020825160e0810184528154815260018201549281018390526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c08301526124bc90151560256145ba565b602081015181516124d491906117be9061271061446d565b979650505050505050565b60216020526000908152604090205481565b60196020526000908152604090205460ff1681565b60256020526000908152604090205481565b6000806125296000861160266145ba565b60008461253e5761253988613eec565b612547565b61254788612de8565b905060008187116125615761255c8288614578565b61256b565b61256b8783614578565b9050600061257d886117be8b8561446d565b9050600087156125905750878311612595565b508288115b60006125ac6010548961452090919063ffffffff16565b42116125d0576001600160a01b038c166000908152601e60205260409020546125d3565b60005b90508180156125f657506125e78b8261446d565b6125f38461271061446d565b11155b1561260057600092505b509a909950975050505050505050565b60026000541415612656576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b600260009081556001600160a01b0382168152601c60205260409020546126819060ff16600e6145ba565b600061268c826148b6565b905061269c60008211600f6145ba565b6126a68282614b25565b604080516001600160a01b03841681526020810183905281517fa5a389190ebf6170a133bda5c769b77f4d6715b8aa172ec0ddf8473d0b4944bd929181900390910190a150506001600055565b601860209081526000928352604080842090915290825290205460ff1681565b60075481565b602e6020526000908152604090205481565b600154630100000090046001600160a01b031681565b3360009081526018602090815260408083206001600160a01b0394909416835292905220805460ff19169055565b620f424081565b61277e614505565b600180546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b6000600260005414156127f2576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b60026000556127ff614dc8565b6001600160a01b0383166000908152601c60205260409020546128269060ff1660136145ba565b6016805461ff00191661010017905560055460009061284d906001600160a01b03166148b6565b905061285d6000821160146145ba565b612867848561417d565b60006128738583611979565b90506128836000821160156145ba565b61288d8583614df7565b6128978582614c3d565b60055460408051632770a7eb60e21b81523060048201526024810185905290516001600160a01b0390921691639dc29fac9160448082019260009290919082900301818387803b1580156128ea57600080fd5b505af11580156128fe573d6000803e3d6000fd5b505060055461291892506001600160a01b03169050614eb5565b6001546040805163eb0835bf60e01b81526001600160a01b038881166004830152602482018690529151600093630100000090049092169163eb0835bf91604480820192602092909190829003018186803b15801561297657600080fd5b505afa15801561298a573d6000803e3d6000fd5b505050506040513d60208110156129a057600080fd5b5051905060006129b1878484614f50565b90506129c16000821160166145ba565b6129cc878288615018565b604080516001600160a01b03808916825289166020820152808201869052606081018390526080810184905290517fd732b7828fa6cee72c285eac756fc66a7477e3dc22e22e7c432f1c265d40b4839181900360a00190a16016805461ff001916905560016000559695505050505050565b612a46614505565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b612a70614505565b60018054612a849160ff90911615906145ba565b6001805460ff191681179055600380546001600160a01b03199081166001600160a01b039889161790915560058054821696881696909617909555600480549095169390951692909217909255600991909155601355601455565b6004546001600160a01b031681565b600a5481565b612afc614505565b60018054911515620100000262ff000019909216919091179055565b600060026000541415612b60576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055612b6d614dc8565b6001600160a01b0383166000908152601c6020526040902054612b949060ff1660106145ba565b6016805461ff0019166101001790556000612bae846148b6565b9050612bbe6000821160116145ba565b612bc8848561417d565b6000612bd385612de8565b90506000612bf168327cb2734119d3b7a9601e1b6117be858561446d565b600554909150612c0d90829088906001600160a01b0316611d24565b9050612c1d6000821160126145ba565b6001546040805163256f6ee360e11b81526001600160a01b0389811660048301526024820185905291516000936301000000900490921691634adeddc691604480820192602092909190829003018186803b158015612c7b57600080fd5b505afa158015612c8f573d6000803e3d6000fd5b505050506040513d6020811015612ca557600080fd5b505190506000612cb6888684614f50565b90506000612cd468327cb2734119d3b7a9601e1b6117be848861446d565b600554909150612cf09082908b906001600160a01b0316611d24565b9050612cfc89826150bf565b612d068983614b25565b600554604080516340c10f1960e01b81526001600160a01b038b8116600483015260248201859052915191909216916340c10f1991604480830192600092919082900301818387803b158015612d5b57600080fd5b505af1158015612d6f573d6000803e3d6000fd5b5050604080516001600160a01b03808d1682528d1660208201528082018a9052606081018590526080810187905290517fab4c77c74cd32c85f35416cf03e7ce9e2d4387f7b7f2c1f4bf53daaecf8ea72d93509081900360a0019150a16016805461ff0019169055600160005598975050505050505050565b60048054601654604080516317e1d38560e11b81526001600160a01b038681169582019590955260006024820181905260ff80851615156044840152610100909404909316151560648201529051919390921691632fc3a70a916084808301926020929190829003018186803b158015612e6157600080fd5b505afa158015612e75573d6000803e3d6000fd5b505050506040513d6020811015612e8b57600080fd5b505192915050565b600060026000541415612edb576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b6002600055612ee8614666565b612ef188614682565b612f008888888888888861517d565b600160005598975050505050505050565b612f19614505565b6001600160a01b03909116600090815260276020526040902055565b601281565b601e6020526000908152604090205481565b612f54614505565b612f64610e10841015600a6145ba565b612f74612710831115600b6145ba565b612f84612710821115600c6145ba565b601292909255601355601455565b602d6020526000908152604090205481565b601d6020526000908152604090205481565b612fbe614505565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60165462010000900460ff1681565b600060026000541415613037576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b600260005560015461305290610100900460ff1660176145ba565b6001600160a01b0384166000908152601c60205260409020546130799060ff1660186145ba565b6001600160a01b0383166000908152601c60205260409020546130a09060ff1660196145ba565b6130c0836001600160a01b0316856001600160a01b03161415601a6145ba565b6016805461ff0019166101001790556130d9848061417d565b6130e3838461417d565b60006130ee856148b6565b90506130fe60008211601b6145ba565b600061310986612de8565b9050600061311686613eec565b90506000613128826117be868661446d565b9050613135818989611d24565b9050600061315368327cb2734119d3b7a9601e1b6117be878761446d565b60055490915061316f9082908b906001600160a01b0316611d24565b60015460408051636d099c0b60e11b81526001600160a01b038d811660048301528c8116602483015260448201859052915193945060009363010000009093049091169163da13381691606480820192602092909190829003018186803b1580156131d957600080fd5b505afa1580156131ed573d6000803e3d6000fd5b505050506040513d602081101561320357600080fd5b5051905060006132148a8584614f50565b90506132208b846150bf565b61322a8a84614df7565b6132348b88614b25565b61323e8a85614c3d565b6132478a61560a565b6132528a828b615018565b604080516001600160a01b03808c168252808e1660208301528c1681830152606081018990526080810186905260a0810183905260c0810184905290517f0874b2d545cb271cdbda4e093020c452328b24af12382ed62c4d00f5c26709db9181900360e00190a16016805461ff001916905560016000559a9950505050505050505050565b68327cb2734119d3b7a9601e1b81565b602f6020526000908152604090205481565b60125481565b60008161330e57506000611831565b611dbf838361331c86613eec565b614143565b6001600160a01b0383166000908152602d6020908152604080832054602e9092528220548285821161335c576133578683614578565b613366565b6133668287614578565b90506000613378836117be868561446d565b905086831160006133898689614520565b90506000826133a15761339c8285614520565b6133ab565b6133ab8285614578565b90506133bb816117be8c8561446d565b9b9a5050505050505050505050565b60115460ff1681565b600d5481565b6000816133e857506000611831565b611dbf838361331c86612de8565b6133fe614505565b6001600160a01b03919091166000908152601a60205260409020805460ff1916911515919091179055565b6012546001600160a01b0382166000908152602a60205260408120549091429161345291614520565b1115613460575060006117c8565b6012546001600160a01b0383166000908152602a6020526040812054909161348d916117be904290614578565b6001600160a01b038416600090815260256020526040902054909150806134b9576000925050506117c8565b6001600160a01b0384166000908152601f602052604081205460ff166134e1576013546134e5565b6014545b6001600160a01b03861660009081526026602052604090205490915061351e9083906117be90869061351890869061446d565b9061446d565b95945050505050565b60165460ff1681565b60226020526000908152604090205481565b60246020526000908152604090205481565b60085481565b601654610100900460ff1681565b6001600160a01b0381166000908152601f602052604081205460ff16156135a857506001600160a01b0381166000908152602560205260409020546117c8565b6001600160a01b0382166000908152602860205260408120546135cc9084906132ff565b6001600160a01b0384166000908152602660209081526040808320546025909252909120549192506117c491613603908490614520565b90614578565b6001546040805163b1cc53ab60e01b81526001600160a01b038681166004830152858116602483015284151560448301529151600093630100000090049092169163b1cc53ab91606480820192602092909190829003018186803b15801561367057600080fd5b505afa158015613684573d6000803e3d6000fd5b505050506040513d602081101561369a57600080fd5b5051949350505050565b6001600160a01b0381166000908152602d60205260408120548190806136d1576000809250925050613737565b60006136dc85613eec565b6001600160a01b0386166000908152602e602052604081205491925082821161370e576137098383614578565b613718565b6137188284614578565b9050600061372a836117be878561446d565b9390921195509193505050505b915091565b60266020526000908152604090205481565b60135481565b60296020526000908152604090205481565b6001546040805163c7e074c360e01b81526001600160a01b03888116600483015260248201889052604482018790526064820186905284151560848301529151600093630100000090049092169163c7e074c39160a480820192602092909190829003018186803b1580156137da57600080fd5b505afa1580156137ee573d6000803e3d6000fd5b505050506040513d602081101561380457600080fd5b50519695505050505050565b613818614505565b61382c6001600160a01b038316848361567f565b505050565b613839614505565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b613863614505565b601755565b613870614505565b61387f612710821160026145ba565b600855565b6001546040805163d54d5a9f60e01b81526001600160a01b03888116600483015287811660248301528681166044830152851515606483015284151560848301528251600094859463010000009091049092169263d54d5a9f9260a4808301939192829003018186803b1580156138fa57600080fd5b505afa15801561390e573d6000803e3d6000fd5b505050506040513d604081101561392457600080fd5b508051602090910151909890975095505050505050565b613943614505565b6001600160a01b0382166000908152602360205260409020548082111561397d57613977836139728484614578565b6150bf565b50613990565b61382c8361398b8385614578565b614df7565b5050565b602a6020526000908152604090205481565b60105481565b6001546040805163369d949360e21b81526001600160a01b0389811660048301528881166024830152878116604483015286151560648301526084820186905260a482018590529151600093630100000090049092169163da76524c9160c480820192602092909190829003018186803b158015613a2957600080fd5b505afa158015613a3d573d6000803e3d6000fd5b505050506040513d6020811015613a5357600080fd5b5051979650505050505050565b601c6020526000908152604090205460ff1681565b602080526000908152604090205460ff1681565b6000806000613a9b8a8a8a8a88612518565b90925090506000613aac8a87614520565b905060008815613adb5783613aca57613ac58284614578565b613ad4565b613ad48284614520565b9050613afc565b83613aef57613aea8284614520565b613af9565b613af98284614578565b90505b613b0a816117be8a8561446d565b9c9b505050505050505050505050565b60155481565b60026000541415613b66576040805162461bcd60e51b815260206004820152601f6024820152600080516020615ded833981519152604482015290519081900360640190fd5b60026000556016546301000000900460ff1615613b9b5733600090815260196020526040902054613b9b9060ff1660226145ba565b6016805460ff19169055613baf848461417d565b6000613bbd868686866119be565b9050613bc7615d9a565b506000818152602b6020908152604091829020825160e08101845281548082526001830154938201939093526002820154938101939093526003810154606084015260048101546080840152600581015460a08401526006015460c0830152613c3390151560236145ba565b600080613c44898989896000613884565b91509150613c57826000141560246145ba565b8160021415613c8c57613c74898989600087600001518b8f61517d565b50506016805460ff1916600117905550613eda915050565b6000613c9889836132ff565b6001600160a01b038a166000908152602c6020526040902054909150613cbe9082614520565b6001600160a01b038a166000818152602c60209081526040918290209390935580519182529181018490528082018390529051600080516020615eb88339815191529181900360600190a1613d178985608001516156d1565b8615613d465760208401518451613d38918b91613d3391614578565b614aa6565b613d468961216d8b856132ff565b600087613d5b57613d5689613eec565b613d64565b613d6489612de8565b90507f2e1f85a64a2f22cf2f0c42584e7c919ed4abe8d53675cff0f62bf1e95a1c676f868c8c8c8c8a600001518b602001518c608001518d60a001518a604051808b81526020018a6001600160a01b03168152602001896001600160a01b03168152602001886001600160a01b0316815260200187151581526020018681526020018581526020018481526020018381526020018281526020019a505050505050505050505060405180910390a187158015613e235750846020015183105b15613e51576020850151600090613e3a9085614578565b9050613e4f8b613e4a8d846132ff565b614b25565b505b87613e6457613e64898660000151615786565b6000868152602b60205260408120818155600181018290556002810182905560038101829055600481018290556005810182905560060155600954613eb0908b9061216d9082906132ff565b613ec68a613ec08c6009546132ff565b89615018565b50506016805460ff19166001179055505050505b50506001600055505050565b600e5481565b60048054601654604080516317e1d38560e11b81526001600160a01b03868116958201959095526001602482015260ff80841615156044830152610100909304909216151560648301525160009390921691632fc3a70a91608480820192602092909190829003018186803b158015612e6157600080fd5b601b8181548110613f7157fe5b6000918252602090912001546001600160a01b0316905081565b613f93614505565b6001600160a01b0381166000908152601c6020526040902054613fba9060ff16600d6145ba565b6001600160a01b038116600090815260226020526040902054601554613fdf91614578565b6015556001600160a01b0381166000908152601c60209081526040808320805460ff19908116909155601d835281842084905560228352818420849055601e835281842084905560248352818420849055601f835281842080548216905591805290912080549091169055600754614058906001614578565b60075550565b614066614505565b6001600160a01b039091166000908152602f6020526040902055565b60286020526000908152604090205481565b61409c614505565b6016805491151563010000000263ff00000019909216919091179055565b60006140c4614505565b6001600160a01b0383166000908152602c6020526040902054806140ec576000915050611831565b6001600160a01b0384166000908152602c6020526040812055611dbf848285615018565b601a6020526000908152604090205460ff1681565b6005546001600160a01b031681565b6003546001600160a01b031681565b60008261415257506000611dbf565b6001600160a01b0384166000908152601d602052604090205461351e836117be86600a85900a61446d565b6001546040805163fbfded6d60e01b81526001600160a01b03858116600483015284811660248301529151600093630100000090049092169163fbfded6d9160448082019260209290919082900301818787803b1580156141dd57600080fd5b505af11580156141f1573d6000803e3d6000fd5b505050506040513d602081101561420757600080fd5b50519050806142165750613990565b6001600160a01b0383166000908152602a6020526040902054614263576012546142449061351842826144c6565b6001600160a01b0384166000908152602a602052604090205550613990565b6012546001600160a01b0384166000908152602a6020526040902054429161428b9190614520565b11156142975750613990565b60006142a284613429565b6001600160a01b0385166000908152602960205260409020549091506142c89082614520565b6001600160a01b0385166000908152602960205260409020556012546142f29061351842826144c6565b6001600160a01b0385166000818152602a602090815260408083209490945560298152908390205483519283529082015281517fa146fc154e1913322e9817d49f0d5c37466c24326e15de10e739a948be815eab929181900390910190a150505050565b610e1081565b6001546040805163fdaf6ac360e01b81526001600160a01b038881166004830152878116602483015286811660448301528515156064830152608482018590529151600093630100000090049092169163fdaf6ac39160a480820192602092909190829003018186803b1580156137da57600080fd5b60306020908152600091825260409182902080548351601f6002600019610100600186161502019093169290920491820184900484028101840190945280845290918301828280156144655780601f1061443a57610100808354040283529160200191614465565b820191906000526020600020905b81548152906001019060200180831161444857829003601f168201915b505050505081565b60008261447c57506000611831565b8282028284828161448957fe5b0414611dbf5760405162461bcd60e51b8152600401808060200182810382526021815260200180615e4d6021913960400191505060405180910390fd5b6000611dbf83836040518060400160405280601a815260200179536166654d6174683a206469766973696f6e206279207a65726f60301b8152506157ee565b60065461451e906001600160a01b0316331460356145ba565b565b600082820183811015611dbf576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b6000611dbf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250615890565b60008181526030602052604090208261382c5760405162461bcd60e51b81526020600482019081528254600260001961010060018416150201909116046024830181905290918291604490910190849080156146575780601f1061462c57610100808354040283529160200191614657565b820191906000526020600020905b81548152906001019060200180831161463a57829003601f168201915b50509250505060405180910390fd5b6017546146725761451e565b61451e6017543a111560376145ba565b336001600160a01b0382161415614698576146e2565b6003546001600160a01b03163314156146b0576146e2565b6001600160a01b03811660009081526018602090815260408083203384529091529020546146e29060ff1660296145ba565b50565b801561475e5761470a826001600160a01b0316846001600160a01b031614602a6145ba565b6001600160a01b0383166000908152601c60205260409020546147319060ff16602b6145ba565b6001600160a01b0383166000908152601f60205260409020546147599060ff1615602c6145ba565b61382c565b6001600160a01b0383166000908152601c60205260409020546147859060ff16602d6145ba565b6001600160a01b0383166000908152601f60205260409020546147ac9060ff16602e6145ba565b6001600160a01b0382166000908152601f60205260409020546147d49060ff1615602f6145ba565b6001600160a01b038216600090815260208052604090205461382c9060ff1660306145ba565b60008061480a898989898961435c565b9050600061481c8a8a8a8a89896139ac565b90506148288282614520565b915060006148368a846132ff565b6001600160a01b038b166000908152602c602052604090205490915061485c9082614520565b6001600160a01b038b166000818152602c60209081526040918290209390935580519182529181018590528082018390529051600080516020615eb88339815191529181900360600190a150909998505050505050505050565b6001600160a01b03811660008181526021602090815260408083205481516370a0823160e01b8152306004820152915193949093859391926370a08231926024808301939192829003018186803b15801561491057600080fd5b505afa158015614924573d6000803e3d6000fd5b505050506040513d602081101561493a57600080fd5b50516001600160a01b03851660009081526021602052604090208190559050611b0b8183614578565b8161497957614974811560276145ba565b613990565b6139908183101560286145ba565b6001600160a01b0382166000908152602660205260409020546149aa9082614520565b6001600160a01b0383166000908152602660208181526040808420859055602582529092205491526149df91111560346145ba565b604080516001600160a01b03841681526020810183905281517faa5649d82f5462be9d19b0f2b31a59b2259950a6076550bac9f3a1c07db9f66d929181900390910190a15050565b6001600160a01b038216600090815260286020526040902054614a4a9082614520565b6001600160a01b03831660008181526028602090815260409182902093909355805191825291810183905281517fd9d4761f75e0d0103b5cbeab941eeb443d7a56a35b5baf2a0787c03f03f4e474929181900390910190a15050565b6001600160a01b038216600090815260286020526040902054614ac99082614578565b6001600160a01b03831660008181526028602090815260409182902093909355805191825291810183905281517f34e07158b9db50df5613e591c44ea2ebc82834eff4a4dc3a46e000e608261d68929181900390910190a15050565b6001600160a01b038216600090815260256020526040902054614b489082614520565b6001600160a01b03831660008181526025602090815260408083209490945583516370a0823160e01b8152306004820152935191936370a082319260248083019392829003018186803b158015614b9e57600080fd5b505afa158015614bb2573d6000803e3d6000fd5b505050506040513d6020811015614bc857600080fd5b50516001600160a01b038416600090815260256020526040902054909150614bf49082101560316145ba565b604080516001600160a01b03851681526020810184905281517f976177fbe09a15e5e43f848844963a42b41ef919ef17ff21a17a5421de8f4737929181900390910190a1505050565b604080518082018252601a81527915985d5b1d0e881c1bdbdb105b5bdd5b9d08195e18d95959195960321b6020808301919091526001600160a01b038516600090815260259091529190912054614c95918390615890565b6001600160a01b03831660009081526025602090815260408083208490556026909152902054614cc891101560326145ba565b604080516001600160a01b03841681526020810183905281517f112726233fbeaeed0f5b1dba5cb0b2b81883dee49fb35ff99fd98ed9f6d31eb0929181900390910190a15050565b6001600160a01b0382166000908152602d6020526040902054614d339082614520565b6001600160a01b0383166000908152602d6020908152604080832093909355602f90522054801561382c576001600160a01b0383166000908152602d602052604090205481101561382c576040805162461bcd60e51b815260206004820152601a60248201527915985d5b1d0e881b585e081cda1bdc9d1cc8195e18d95959195960321b604482015290519081900360640190fd5b60165462010000900460ff161561451e57336000908152601a602052604090205461451e9060ff1660366145ba565b6001600160a01b038216600090815260236020526040902054818111614e60576001600160a01b038316600081815260236020908152604080832092909255815192835282018390528051600080516020615e6e8339815191529281900390910190a150613990565b614e6a8183614578565b6001600160a01b0384166000818152602360209081526040918290209390935580519182529181018490528151600080516020615e6e833981519152929181900390910190a1505050565b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015614f0457600080fd5b505afa158015614f18573d6000803e3d6000fd5b505050506040513d6020811015614f2e57600080fd5b50516001600160a01b0390921660009081526021602052604090209190915550565b600080614f6d6127106117be614f668287614578565b879061446d565b90506000614f7b8583614578565b6001600160a01b0387166000908152602c6020526040902054909150614fa19082614520565b6001600160a01b0387166000908152602c60205260409020557f47cd9dda0e50ce30bcaaacd0488452b596221c07ac402a581cfae4d3933cac2b86614fe681846117e4565b604080516001600160a01b0390931683526020830191909152818101849052519081900360600190a150949350505050565b61502c6001600160a01b038416828461567f565b604080516370a0823160e01b815230600482015290516001600160a01b038516916370a08231916024808301926020929190829003018186803b15801561507257600080fd5b505afa158015615086573d6000803e3d6000fd5b505050506040513d602081101561509c57600080fd5b50516001600160a01b039093166000908152602160205260409020929092555050565b6001600160a01b0382166000908152602360205260409020546150e29082614520565b6001600160a01b0383166000908152602360209081526040808320939093556024905220548015615134576001600160a01b0383166000908152602360205260409020546151349082101560336145ba565b604080516001600160a01b03851681526020810184905281517f64243679a443432e2293343b77d411ff6144370404618f00ca0d2025d9ca9882929181900390910190a1505050565b600154604080516381d11a2360e01b81526001600160a01b038a8116600483015289811660248301528881166044830152606482018890526084820187905285151560a483015284811660c4830152915160009363010000009004909216916381d11a239160e4808201928692909190829003018186803b15801561520157600080fd5b505afa158015615215573d6000803e3d6000fd5b50505050615223878761417d565b6000615231898989876119be565b6000818152602b60205260409020805491925090615252901515601f6145ba565b615264868260000154101560206145ba565b615276878260010154101560216145ba565b600181015481546004830154600091615293916117be908b61446d565b60048401549091506152a59082614578565b60048401556152b48b826156d1565b506000806152c68d8d8d8d8d8d6158ea565b855491935091508914615446576152de8c8c8a613609565b600385015583546152ef908a614578565b80855560018501546153019190614963565b61530f8d8d8d8b6001613884565b5050871561533c576153328c61214686600101548661457890919063ffffffff16565b61533c8c8a614aa6565b6000886153515761534c8c613eec565b61535a565b61535a8c612de8565b9050600080516020615e2d833981519152868f8f8f8f8f8f8861537d8c8c614578565b60408051998a526001600160a01b0398891660208b015296881689880152949096166060880152608087019290925260a0860152151560c085015260e084019290925261010083019190915251908190036101200190a18454600186015460028701546003880154600489015460058a0154604080518d81526020810197909752868101959095526060860193909352608085019190915260a084015260c083015260e0820183905251600080516020615e0d833981519152918190036101000190a1506155a8565b8715615460576154568c84614a27565b6154608c8a614aa6565b600088615475576154708c613eec565b61547e565b61547e8c612de8565b9050600080516020615e2d833981519152868f8f8f8f8f8f886154a18c8c614578565b60408051998a526001600160a01b0398891660208b015296881689880152949096166060880152608087019290925260a0860152151560c085015260e084019290925261010083019190915251908190036101200190a18454600186015460028701546003880154600489015460058a0154604080518d81526020810197909752868101959095526060860193909352608085019190915260a084015260c0830152517f73af1d417d82c240fdb6d319b34ad884487c6bf2845d98980cc52ad9171cb4559181900360e00190a1506000858152602b602052604081208181556001810182905560028101829055600381018290556004810182905560058101829055600601555b876155b7576155b78b8a615786565b81156155f75787156155d1576155d18c61216d8e856132ff565b60006155dd8d836132ff565b90506155ea8d828a615018565b95506124d4945050505050565b5060009c9b505050505050505050505050565b6001600160a01b03811660009081526027602090815260408083205460259092529091205410156146e2576040805162461bcd60e51b815260206004820152601a6024820152792b30bab63a1d103837b7b620b6b7bab73a101e10313ab33332b960311b604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261382c908490615ae4565b604080518082018252601b81527a5661756c743a20696e73756666696369656e74207265736572766560281b6020808301919091526001600160a01b03851660009081526026909152919091205461572a918390615890565b6001600160a01b03831660008181526026602090815260409182902093909355805191825291810183905281517f533cb5ed32be6a90284e96b5747a1bfc2d38fdb5768a6b5f67ff7d62144ed67b929181900390910190a15050565b6001600160a01b0382166000908152602d6020526040902054808211156157c657506001600160a01b0382166000908152602d6020526040812055613990565b6157d08183614578565b6001600160a01b0384166000908152602d6020526040902055505050565b6000818361587a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561583f578181015183820152602001615827565b50505050905090810190601f16801561586c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161588657fe5b0495945050505050565b600081848411156158e25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561583f578181015183820152602001615827565b505050900390565b60008060006158fb898989876119be565b6000818152602b60205260408120805460038201549394509092615928918d918d918d918b918d916147fa565b90506000806000806159498d876000015488600201548d8a60060154612518565b87549195508593509150615961906117be8d8461446d565b9250505060008280156159745750600082115b156159a457506005840180548201905580886159a45760006159968e846132ff565b90506159a28e82614c3d565b505b821580156159b25750600082115b156159f65760018501546159c69083614578565b6001860155886159ea5760006159dc8e846132ff565b90506159e88e82614b25565b505b60058501805483900390555b8a15615a1e57615a06818c614520565b6001860154909150615a18908c614578565b60018601555b84548a1415615a41576001850154615a37908290614520565b6000600187015590505b8084811115615a5b57615a548286614578565b9050615a8f565b6001860154615a6a9086614578565b60018701558915615a8f576000615a818f876132ff565b9050615a8d8f82614c3d565b505b60408051888152851515602082015280820185905290517f3ff41bdde87755b687ae83d0221a232b6be51a803330ed9661c1b5d0105e0d8a9181900360600190a1909e909d509b505050505050505050505050565b6060615b39826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316615b959092919063ffffffff16565b80519091501561382c57808060200190516020811015615b5857600080fd5b505161382c5760405162461bcd60e51b815260040180806020018281038252602a815260200180615e8e602a913960400191505060405180910390fd5b6060611b0b848460008585615ba985615cb0565b615bfa576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310615c395780518252601f199092019160209182019101615c1a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114615c9b576040519150601f19603f3d011682016040523d82523d6000602084013e615ca0565b606091505b50915091506124d4828286615cb6565b3b151590565b60608315615cc5575081611dbf565b825115615cd55782518084602001fd5b60405162461bcd60e51b815260206004820181815284516024840152845185939192839260440191908501908083836000831561583f578181015183820152602001615827565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615d5d5782800160ff19823516178555615d8a565b82800160010185558215615d8a579182015b82811115615d8a578235825591602001919060010190615d6f565b50615d96929150615dd7565b5090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b5b80821115615d965760008155600101615dd856fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c0020853733b590dce729d9f4628682ebd9a34d2354e72679e66f43a008fc03b77393d75d64d1f84fc6f430a64fc578bdd4c1e090e90ea2d51773e626d19de56d30536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77e1e812596aac93a06ecc4ca627014d18e30f5c33b825160cc9d5c0ba61e452275361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645d0c0019d3d45fadeb74eff9d2c9924d146d000ac6bcf3c28bf0ac3c9baa011aa2646970667358221220127f467dd7997c47df98a559f890d614307e268cbd07feca4f7c83135a70dc4664736f6c634300060c0033
Deployed Bytecode Sourcemap
404:54466:17:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37666:260;;;;;;;;;;;;;;;;-1:-1:-1;37666:260:17;-1:-1:-1;;;;;37666:260:17;;:::i;:::-;;;;;;;;;;;;;;;;1116:71;;;:::i;9784:132::-;;;:::i;33938:312::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;33938:312:17;;;;;;;;:::i;1935:49::-;;;:::i;768:52::-;;;:::i;1675:27::-;;;:::i;:::-;;;;-1:-1:-1;;;;;1675:27:17;;;;;;;;;;;;;;2461:47;;;:::i;1829:41::-;;;:::i;2698:53::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;3736:56;;;;;;;;;;;;;;;;-1:-1:-1;3736:56:17;-1:-1:-1;;;;;3736:56:17;;:::i;5539:::-;;;;;;;;;;;;;;;;-1:-1:-1;5539:56:17;-1:-1:-1;;;;;5539:56:17;;:::i;9924:135::-;;;;;;;;;;;;;;;;-1:-1:-1;9924:135:17;;;;:::i;:::-;;15781:107;;;;;;;;;;;;;;;;-1:-1:-1;15781:107:17;-1:-1:-1;;;;;15781:107:17;;:::i;9567:209::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;9567:209:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;9567:209:17;;;;;;;;;;-1:-1:-1;9567:209:17;;-1:-1:-1;9567:209:17;-1:-1:-1;9567:209:17;:::i;33383:162::-;;;;;;;;;;;;;;;;-1:-1:-1;33383:162:17;-1:-1:-1;;;;;33383:162:17;;:::i;32726:307::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;32726:307:17;;;;;;;;:::i;35796:299::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;35796:299:17;;;;;;;;;;;;;;;;;;;;;;;;:::i;10570:135::-;;;;;;;;;;;;;;;;-1:-1:-1;10570:135:17;;;;:::i;2195:49::-;;;:::i;1382:41::-;;;:::i;1341:34::-;;;:::i;44069:293::-;;;;;;;;;;;;;;;;-1:-1:-1;44069:293:17;-1:-1:-1;;;;;44069:293:17;;:::i;13655:1233::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;13655:1233:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;2760:35::-;;;:::i;1430:45::-;;;:::i;11749:1357::-;;;;;;;;;;;;;;;;-1:-1:-1;11749:1357:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;33553:377::-;;;;;;;;;;;;;;;;-1:-1:-1;33553:377:17;;;-1:-1:-1;;;;;33553:377:17;;;;;;;;;;;;:::i;3268:54::-;;;;;;;;;;;;;;;;-1:-1:-1;3268:54:17;-1:-1:-1;;;;;3268:54:17;;:::i;10407:155::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;10407:155:17;;;;;;;;;;:::i;40646:404::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;40646:404:17;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;21528:3807;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;21528:3807:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;1522:30::-;;;:::i;34967:821::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;34967:821:17;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4545:58;;;;;;;;;;;;;;;;-1:-1:-1;4545:58:17;-1:-1:-1;;;;;4545:58:17;;:::i;1053:50::-;;;:::i;1999:51::-;;;:::i;5428:46::-;;;;;;;;;;;;;;;;-1:-1:-1;5428:46:17;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37934:418;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;37934:418:17;;;;;;;;;;;;;;;;;;;;;;;;:::i;3462:58::-;;;;;;;;;;;;;;;;-1:-1:-1;3462:58:17;-1:-1:-1;;;;;3462:58:17;;:::i;2889:54::-;;;;;;;;;;;;;;;;-1:-1:-1;2889:54:17;-1:-1:-1;;;;;2889:54:17;;:::i;4143:56::-;;;;;;;;;;;;;;;;-1:-1:-1;4143:56:17;-1:-1:-1;;;;;4143:56:17;;:::i;41058:1097::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;41058:1097:17;;;;;;;;;;;;;;;;;;;;;;;;;:::i;16752:331::-;;;;;;;;;;;;;;;;-1:-1:-1;16752:331:17;-1:-1:-1;;;;;16752:331:17;;:::i;2804:78::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;2804:78:17;;;;;;;;;;:::i;1711:45::-;;;:::i;5672:69::-;;;;;;;;;;;;;;;;-1:-1:-1;5672:69:17;-1:-1:-1;;;;;5672:69:17;;:::i;1484:29::-;;;:::i;15896:111::-;;;;;;;;;;;;;;;;-1:-1:-1;15896:111:17;-1:-1:-1;;;;;15896:111:17;;:::i;827:56::-;;;:::i;9284:130::-;;;;;;;;;;;;;;;;-1:-1:-1;9284:130:17;-1:-1:-1;;;;;9284:130:17;;:::i;18339:1425::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;18339:1425:17;;;;;;;;;;:::i;11107:122::-;;;;;;;;;;;;;;;;-1:-1:-1;11107:122:17;-1:-1:-1;;;;;11107:122:17;;:::i;8701:575::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;8701:575:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;1598:33::-;;;:::i;1877:43::-;;;:::i;10713:151::-;;;;;;;;;;;;;;;;-1:-1:-1;10713:151:17;;;;:::i;17091:1240::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;17091:1240:17;;;;;;;;;;:::i;32533:185::-;;;;;;;;;;;;;;;;-1:-1:-1;32533:185:17;-1:-1:-1;;;;;32533:185:17;;:::i;25343:419::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;25343:419:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;11427:147::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;11427:147:17;;;;;;;;:::i;1004:42::-;;;:::i;3196:65::-;;;;;;;;;;;;;;;;-1:-1:-1;3196:65:17;-1:-1:-1;;;;;3196:65:17;;:::i;13114:533::-;;;;;;;;;;;;;;;;-1:-1:-1;13114:533:17;;;;;;;;;;;;:::i;5604:61::-;;;;;;;;;;;;;;;;-1:-1:-1;5604:61:17;-1:-1:-1;;;;;5604:61:17;;:::i;3131:58::-;;;;;;;;;;;;;;;;-1:-1:-1;3131:58:17;-1:-1:-1;;;;;3131:58:17;;:::i;9422:137::-;;;;;;;;;;;;;;;;-1:-1:-1;9422:137:17;-1:-1:-1;;;;;9422:137:17;;:::i;2649:42::-;;;:::i;19772:1748::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;19772:1748:17;;;;;;;;;;;;;;;;;;;:::i;890:50::-;;;:::i;5748:64::-;;;;;;;;;;;;;;;;-1:-1:-1;5748:64:17;-1:-1:-1;;;;;5748:64:17;;:::i;2357:49::-;;;:::i;34476:210::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;34476:210:17;;;;;;;;:::i;39362:693::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;39362:693:17;;;;;;;;;;;;;:::i;2305:43::-;;;:::i;2065:47::-;;;:::i;34258:210::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;34258:210:17;;;;;;;;:::i;10067:145::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;10067:145:17;;;;;;;;;;:::i;37077:581::-;;;;;;;;;;;;;;;;-1:-1:-1;37077:581:17;-1:-1:-1;;;;;37077:581:17;;:::i;2565:34::-;;;:::i;3592:57::-;;;;;;;;;;;;;;;;-1:-1:-1;3592:57:17;-1:-1:-1;;;;;3592:57:17;;:::i;3877:59::-;;;;;;;;;;;;;;;;-1:-1:-1;3877:59:17;-1:-1:-1;;;;;3877:59:17;;:::i;1765:48::-;;;:::i;2606:34::-;;;:::i;33041:334::-;;;;;;;;;;;;;;;;-1:-1:-1;33041:334:17;-1:-1:-1;;;;;33041:334:17;;:::i;42163:216::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;42163:216:17;;;;;;;;;;;;;;;;;;;:::i;40063:575::-;;;;;;;;;;;;;;;;-1:-1:-1;40063:575:17;-1:-1:-1;;;;;40063:575:17;;:::i;4297:60::-;;;;;;;;;;;;;;;;-1:-1:-1;4297:60:17;-1:-1:-1;;;;;4297:60:17;;:::i;2413:41::-;;;:::i;5162:67::-;;;;;;;;;;;;;;;;-1:-1:-1;5162:67:17;-1:-1:-1;;;;;5162:67:17;;:::i;43772:289::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;43772:289:17;;;;;;;;;;;;;;;;;;;;;;;;;:::i;16452:170::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;16452:170:17;;;;;;;;;;;;;;;;;:::i;11010:89::-;;;;;;;;;;;;;;;;-1:-1:-1;11010:89:17;-1:-1:-1;;;;;11010:89:17;;:::i;10872:130::-;;;;;;;;;;;;;;;;-1:-1:-1;10872:130:17;;:::i;11237:182::-;;;;;;;;;;;;;;;;-1:-1:-1;11237:182:17;;:::i;32050:283::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;32050:283:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;16015:357;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;16015:357:17;;;;;;;;:::i;5314:61::-;;;;;;;;;;;;;;;;-1:-1:-1;5314:61:17;-1:-1:-1;;;;;5314:61:17;;:::i;2261:37::-;;;:::i;42387:300::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;42387:300:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;3065:59::-;;;;;;;;;;;;;;;;-1:-1:-1;3065:59:17;-1:-1:-1;;;;;3065:59:17;;:::i;3329:57::-;;;;;;;;;;;;;;;;-1:-1:-1;3329:57:17;-1:-1:-1;;;;;3329:57:17;;:::i;38524:666::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;38524:666:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;2515:41::-;;;:::i;29204:2788::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;29204:2788:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;2127:52::-;;;:::i;32341:184::-;;;;;;;;;;;;;;;;-1:-1:-1;32341:184:17;-1:-1:-1;;;;;32341:184:17;;:::i;3010:46::-;;;;;;;;;;;;;;;;-1:-1:-1;3010:46:17;;:::i;14896:553::-;;;;;;;;;;;;;;;;-1:-1:-1;14896:553:17;-1:-1:-1;;;;;14896:553:17;;:::i;11582:159::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;11582:159:17;;;;;;;;:::i;5018:58::-;;;;;;;;;;;;;;;;-1:-1:-1;5018:58:17;-1:-1:-1;;;;;5018:58:17;;:::i;10220:179::-;;;;;;;;;;;;;;;;-1:-1:-1;10220:179:17;;;;:::i;15457:316::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;15457:316:17;;;;;;;;;;:::i;2950:51::-;;;;;;;;;;;;;;;;-1:-1:-1;2950:51:17;-1:-1:-1;;;;;2950:51:17;;:::i;1640:28::-;;;:::i;1561:30::-;;;:::i;34694:265::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;34694:265:17;;;;;;;;;;;;;:::i;36103:966::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;36103:966:17;;;;;;;;;;:::i;1205:59::-;;;:::i;42695:266::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;42695:266:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;5821:41::-;;;;;;;;;;;;;;;;-1:-1:-1;5821:41:17;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37666:260;-1:-1:-1;;;;;37768:19:17;;37727:7;37768:19;;;:11;:19;;;;;;37802:15;37798:34;;37828:1;37821:8;;;;;37798:34;-1:-1:-1;;;;;37851:23:17;;;;;;:15;:23;;;;;;:67;;37907:10;;37851:51;;876:7;37851:27;:51::i;:::-;:55;;:67::i;:::-;37844:74;;;37666:260;;;;:::o;1116:71::-;-1:-1:-1;;;1116:71:17;:::o;9784:132::-;9881:20;:27;9784:132;:::o;33938:312::-;34029:7;34053:17;34049:36;;-1:-1:-1;34081:1:17;34074:8;;34049:36;34095:13;34111:19;34123:6;34111:11;:19::i;:::-;-1:-1:-1;;;;;34160:21:17;;34141:16;34160:21;;;:13;:21;;;;;;34095:35;;-1:-1:-1;34199:43:17;34227:2;:14;;;34199:23;:12;34095:35;34199:16;:23::i;:43::-;34192:50;;;;33938:312;;;;;:::o;1935:49::-;;;;:::o;768:52::-;815:5;768:52;:::o;1675:27::-;;;-1:-1:-1;;;;;1675:27:17;;:::o;2461:47::-;;;;:::o;1829:41::-;;;;:::o;2698:53::-;;;;;;;;;:::o;3736:56::-;;;;;;;;;;;;;:::o;5539:::-;;;;;;;;;;;;;:::o;9924:135::-;10000:10;:8;:10::i;:::-;10021:13;:30;;;;;;;-1:-1:-1;;10021:30:17;;;;;;;;;9924:135::o;15781:107::-;15853:10;15837:27;;;;:15;:27;;;;;;;;-1:-1:-1;;;;;15837:36:17;;;;;;;;;;:43;;-1:-1:-1;;15837:43:17;15876:4;15837:43;;;15781:107::o;9567:209::-;9680:15;;-1:-1:-1;;;;;9680:15:17;9666:10;:29;9658:72;;;;;-1:-1:-1;;;9658:72:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;9741:18;;;;:6;:18;;;;;:27;;9762:6;;9741:27;:::i;:::-;;9567:209;;;:::o;33383:162::-;33456:7;33483:54;33497:6;33505:31;33529:6;33505:23;:31::i;:::-;33483:13;:54::i;32726:307::-;32822:7;32842:13;32858:19;32870:6;32858:11;:19::i;:::-;32842:35;-1:-1:-1;32888:24:17;32915:43;32842:35;32915:32;:11;-1:-1:-1;;;32915:15:17;:32::i;:43::-;33012:4;;32888:70;;-1:-1:-1;32976:49:17;;32888:70;;-1:-1:-1;;;;;33012:4:17;33018:6;32976:17;:49::i;35796:299::-;35957:129;;;-1:-1:-1;;;;;;35957:129:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;35947:140;;;;;;35796:299::o;10570:135::-;10646:10;:8;:10::i;:::-;10667:13;:30;;;;;;;-1:-1:-1;;10667:30:17;;;;;;;;;10570:135::o;2195:49::-;;;;:::o;1382:41::-;;;;;;;;;:::o;1341:34::-;;;;;;:::o;44069:293::-;44144:7;44164:14;44188:4;;;;;;;;;-1:-1:-1;;;;;44188:4:17;-1:-1:-1;;;;;44181:24:17;;:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;44181:26:17;;-1:-1:-1;44222:11:17;44218:30;;44244:1;44237:8;;;;;44218:30;-1:-1:-1;;;;;44275:20:17;;44258:14;44275:20;;;:12;:20;;;;;;44336:17;;44313:41;;:18;44275:20;44324:6;44313:10;:18::i;:41::-;44306:48;44069:293;-1:-1:-1;;;;44069:293:17:o;13655:1233::-;13922:10;:8;:10::i;:::-;-1:-1:-1;;;;;14001:25:17;;;;;;:17;:25;;;;;;;;13996:159;;14067:21;;:28;;14093:1;14067:25;:28::i;:::-;14043:21;:52;14110:20;:33;;;;;;;-1:-1:-1;14110:33:17;;;;;;;;-1:-1:-1;;;;;;14110:33:17;-1:-1:-1;;;;;14110:33:17;;;;;13996:159;14196:17;;-1:-1:-1;;;;;14268:20:17;;14167:26;14268:20;;;:12;:20;;;;;;14245:44;;14196:17;;14245:22;:44::i;:::-;-1:-1:-1;;;;;14302:25:17;;;;;;:17;:25;;;;;;;;:32;;14330:4;-1:-1:-1;;14302:32:17;;;;;;;14345:13;:21;;;;;:38;;;14394:12;:20;;;;;:35;;;14440:20;:28;;;;;:44;;;14495:14;:22;;;;;:39;;;14545:12;:20;;;;;:32;;;;;;;;;;14588:23;;;;;;:38;;;;;;;;;;;14224:65;-1:-1:-1;14780:36:17;14224:65;14394:35;14780:22;:36::i;:::-;14760:17;:56;14861:19;14873:6;14861:11;:19::i;:::-;;13655:1233;;;;;;;;:::o;2760:35::-;;;;:::o;1430:45::-;;;;;;;;;:::o;11749:1357::-;12141:10;:8;:10::i;:::-;12162:53;1100:3;12172:15;:39;;12213:1;12162:9;:53::i;:::-;12226:59;1100:3;12236:21;:45;;12283:1;12226:9;:59::i;:::-;12296:61;1100:3;12306:23;:47;;12355:1;12296:9;:61::i;:::-;12368:57;1100:3;12378:19;:43;;12423:1;12368:9;:57::i;:::-;12436:63;1100:3;12446:25;:49;;12497:1;12436:9;:63::i;:::-;12510:59;1100:3;12520:21;:45;;12567:1;12510:9;:59::i;:::-;12580;-1:-1:-1;;;12590:45:17;;;12637:1;12580:9;:59::i;:::-;12650:14;:32;;;;12693:20;:44;;;;12748:22;:48;;;;12807:18;:40;;;;12858:24;:52;12921:20;:44;12976:17;:38;13025:13;:30;13066:14;:32;;-1:-1:-1;;13066:32:17;;;;;;;;;;11749:1357::o;33553:377::-;33711:4;;33656:7;;;;-1:-1:-1;;;;;33698:17:17;;;33711:4;;33698:17;:60;;-1:-1:-1;;;;;33734:24:17;;;;;;:13;:24;;;;;;33698:60;;;1044:2;33698:60;33804:4;;33676:82;;-1:-1:-1;33769:19:17;;-1:-1:-1;;;;;33791:17:17;;;33804:4;;33791:17;:60;;-1:-1:-1;;;;;33827:24:17;;;;;;:13;:24;;;;;;33791:60;;;1044:2;33791:60;33769:82;;33869:53;33910:11;33904:2;:17;33869:30;33887:11;33881:2;:17;33869:7;:11;;:30;;;;:::i;:53::-;33862:60;;;;33553:377;;;;;;:::o;3268:54::-;;;;;;;;;;;;;;;:::o;10407:155::-;10496:10;:8;:10::i;:::-;-1:-1:-1;;;;;10517:25:17;;;;;;;;:12;:25;;;;;:37;;-1:-1:-1;;10517:37:17;;;;;;;;;;10407:155::o;40646:404::-;40772:4;40778:7;40798:11;40812:64;40827:8;40837:16;40855:11;40868:7;40812:14;:64::i;:::-;40798:78;;40887:24;;:::i;:::-;-1:-1:-1;40914:14:17;;;;:9;:14;;;;;;;;;40887:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;40946:96;;40955:11;;40887:41;;;41006:7;;40946:8;:96::i;:::-;40939:103;;;;;;40646:404;;;;;;;:::o;21528:3807::-;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;21705:17:17::1;::::0;21695:32:::1;::::0;21705:17;;::::1;;;21724:2;21695:9;:32::i;:::-;21738:19;:17;:19::i;:::-;21768:25;21784:8;21768:15;:25::i;:::-;21804:55;21820:16;21838:11;21851:7;21804:15;:55::i;:::-;21870:10;::::0;:97:::1;::::0;;-1:-1:-1;;;21870:97:17;;-1:-1:-1;;;;;21870:97:17;;::::1;;::::0;::::1;::::0;;;::::1;::::0;;;;;;::::1;::::0;;;;;;;;;;;::::1;;::::0;;;;;;:10;;;::::1;::::0;;::::1;::::0;:35:::1;::::0;:97;;;;;-1:-1:-1;;21870:97:17;;;;;;;;:10;:97;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;21980:58;22008:16;22026:11;21980:27;:58::i;:::-;22051:11;22065:64;22080:8;22090:16;22108:11;22121:7;22065:14;:64::i;:::-;22140:25;22168:14:::0;;;:9:::1;:14;::::0;;;;22051:78;;-1:-1:-1;22211:7:17;:61:::1;;22248:24;22260:11;22248;:24::i;:::-;22211:61;;;22221:24;22233:11;22221;:24::i;:::-;22289:13:::0;;22195:77;;-1:-1:-1;22285:80:17::1;;22324:21;::::0;::::1;:29:::0;;;22285:80:::1;22381:13:::0;;:17;;;;:35:::1;;;22415:1;22402:10;:14;22381:35;22377:218;;;22457:126;22477:11;22490:8;:13;;;22505:8;:21;;;22528:7;22537:5;22544:10;22556:8;:26;;;22457:19;:126::i;:::-;22433:21;::::0;::::1;:150:::0;22377:218:::1;22607:11;22621:122;22640:8;22650:16;22668:11;22681:7;22690:10;22702:8;:13;;;22717:8;:25;;;22621:18;:122::i;:::-;22607:136;;22754:23;22780:29;22792:16;22780:11;:29::i;:::-;22754:55;;22820:26;22849:48;22863:16;22881:15;22849:13;:48::i;:::-;22932:19;::::0;::::1;::::0;22820:77;;-1:-1:-1;22932:43:17::1;::::0;22820:77;22932:23:::1;:43::i;:::-;22910:19;::::0;::::1;:65:::0;;;22986:41:::1;::::0;22996:26;-1:-1:-1;22996:26:17::1;23024:2;22986:9;:41::i;:::-;23062:19;::::0;::::1;::::0;:28:::1;::::0;23086:3;23062:23:::1;:28::i;:::-;23040:19;::::0;::::1;:50:::0;23129:59:::1;23149:16:::0;23167:11;23180:7;23129:19:::1;:59::i;:::-;23101:25;::::0;::::1;:87:::0;23215:13;;:29:::1;::::0;23233:10;23215:17:::1;:29::i;:::-;23199:45:::0;;;23284:15:::1;23255:26;::::0;::::1;:44:::0;23312:32:::1;::::0;23322:17;;23341:2:::1;23312:9;:32::i;:::-;23355:53;23373:8;:13;;;23388:8;:19;;;23355:17;:53::i;:::-;23419:75;23439:8;23449:16;23467:11;23480:7;23489:4;23419:19;:75::i;:::-;;;23565:20;23588:43;23602:16;23620:10;23588:13;:43::i;:::-;23667:22;::::0;::::1;::::0;23565:66;;-1:-1:-1;23667:40:17::1;::::0;23565:66;23667:26:::1;:40::i;:::-;23642:22;::::0;::::1;:65:::0;23718:55:::1;23742:16:::0;23760:12;23718:23:::1;:55::i;:::-;23790:7;23786:1235;;;24120:61;24143:16:::0;24161:19:::1;:10:::0;24176:3;24161:14:::1;:19::i;:::-;24120:22;:61::i;:::-;24196:60;24219:16;24237:18;24196:22;:60::i;:::-;24338:54;24358:16;24376:15;24338:19;:54::i;:::-;24573:75;24593:16;24611:36;24625:16;24643:3;24611:13;:36::i;:::-;24573:19;:75::i;:::-;23786:1235;;;-1:-1:-1::0;;;;;24685:29:17;::::1;;::::0;;;:16:::1;:29;::::0;;;;;24681:263:::1;;-1:-1:-1::0;;;;;24740:37:17;::::1;;::::0;;;:24:::1;:37;::::0;;;;:45;;;24681:263:::1;;;24866:62;24897:11;24910:5;24917:10;24866:30;:62::i;:::-;-1:-1:-1::0;;;;;24826:37:17;::::1;;::::0;;;:24:::1;:37;::::0;;;;:102;24681:263:::1;24960:49;24985:11;24998:10;24960:24;:49::i;:::-;25038:115;::::0;;;;;-1:-1:-1;;;;;25038:115:17;;::::1;;::::0;::::1;::::0;;;::::1;::::0;;;;;::::1;::::0;;;;;;;;;;;;;;;;;::::1;;::::0;;;;;;;;;;;;;;;;;;::::1;::::0;;;;;;;::::1;25189:13:::0;;25204:19:::1;::::0;::::1;::::0;25225:21:::1;::::0;::::1;::::0;25248:25:::1;::::0;::::1;::::0;25275:22:::1;::::0;::::1;::::0;25299:20:::1;::::0;::::1;::::0;25169:158:::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;25169:158:17;;;;;;;::::1;-1:-1:-1::0;;1660:1:64;2622:7;:22;-1:-1:-1;;;;;;;;;;21528:3807:17:o;1522:30::-;;;-1:-1:-1;;;;;1522:30:17;;:::o;34967:821::-;35097:7;35106;35115;35124;35133;35142;35151:4;35157:7;35177:11;35191:64;35206:8;35216:16;35234:11;35247:7;35191:14;:64::i;:::-;35177:78;;35266:24;;:::i;:::-;35293:9;:14;35303:3;35293:14;;;;;;;;;;;35266:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;35318:19;35363:1;35340:8;:20;;;:24;:89;;35408:8;:20;;;35407:21;;35340:89;;;35375:8;:20;;;35340:89;35318:111;;35462:8;:13;;;35495:8;:19;;;35534:8;:21;;;35575:8;:25;;;35620:8;:22;;;35662:11;35717:1;35693:8;:20;;;:25;;35738:8;:26;;;35440:340;;;;;;;;;;;;;;;;;;;34967:821;;;;;;;;;;;;;:::o;4545:58::-;;;;;;;;;;;;;:::o;1053:50::-;1100:3;1053:50;:::o;1999:51::-;;;;:::o;5428:46::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;37934:418::-;38063:7;38083:11;38097:64;38112:8;38122:16;38140:11;38153:7;38097:14;:64::i;:::-;38083:78;;38172:24;;:::i;:::-;-1:-1:-1;38199:14:17;;;;:9;:14;;;;;;;;;38172:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38224:38;;38234:23;;38259:2;38224:9;:38::i;:::-;38324:19;;;;38280:13;;:64;;38324:19;38280:39;;815:5;38280:17;:39::i;:64::-;38273:71;37934:418;-1:-1:-1;;;;;;;37934:418:17:o;3462:58::-;;;;;;;;;;;;;:::o;2889:54::-;;;;;;;;;;;;;;;:::o;4143:56::-;;;;;;;;;;;;;:::o;41058:1097::-;41207:4;41213:7;41233:32;41259:1;41243:13;:17;41262:2;41233:9;:32::i;:::-;41276:13;41292:7;:61;;41329:24;41341:11;41329;:24::i;:::-;41292:61;;;41302:24;41314:11;41302;:24::i;:::-;41276:77;;41364:18;41401:5;41385:13;:21;:75;;41436:24;:5;41446:13;41436:9;:24::i;:::-;41385:75;;;41409:24;:13;41427:5;41409:17;:24::i;:::-;41364:96;-1:-1:-1;41471:13:17;41487:40;41513:13;41487:21;:5;41364:96;41487:9;:21::i;:40::-;41471:56;;41540:14;41571:7;41567:139;;;-1:-1:-1;41607:21:17;;;41567:139;;;-1:-1:-1;41673:21:17;;;41567:139;41880:14;41915:37;41938:13;;41915:18;:22;;:37;;;;:::i;:::-;41897:15;:55;:95;;-1:-1:-1;;;;;41959:33:17;;;;;;:20;:33;;;;;;41897:95;;;41955:1;41897:95;41880:112;;42007:9;:65;;;;-1:-1:-1;42055:17:17;:5;42065:6;42055:9;:17::i;:::-;42020:31;:5;815;42020:9;:31::i;:::-;:52;;42007:65;42003:107;;;42097:1;42089:9;;42003:107;-1:-1:-1;42130:9:17;42141:5;;-1:-1:-1;41058:1097:17;-1:-1:-1;;;;;;;;41058:1097:17:o;16752:331::-;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;;;-1:-1:-1;;;;;16847:25:17;::::1;::::0;;:17:::1;:25;::::0;;;;;16837:40:::1;::::0;16847:25:::1;;16874:2;16837:9;:40::i;:::-;16888:19;16910;16922:6;16910:11;:19::i;:::-;16888:41;;16940:30;16964:1;16950:11;:15;16967:2;16940:9;:30::i;:::-;16981:40;17001:6;17009:11;16981:19;:40::i;:::-;17037:38;::::0;;-1:-1:-1;;;;;17037:38:17;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;;;;;;;;::::1;-1:-1:-1::0;;1660:1:64;2622:7;:22;16752:331:17:o;2804:78::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1711:45::-;;;;:::o;5672:69::-;;;;;;;;;;;;;:::o;1484:29::-;;;;;;-1:-1:-1;;;;;1484:29:17;;:::o;15896:111::-;15971:10;15994:5;15955:27;;;:15;:27;;;;;;;;-1:-1:-1;;;;;15955:36:17;;;;;;;;;;:44;;-1:-1:-1;;15955:44:17;;;15896:111::o;827:56::-;876:7;827:56;:::o;9284:130::-;9361:10;:8;:10::i;:::-;9382;:24;;-1:-1:-1;;;;;9382:24:17;;;;;-1:-1:-1;;;;;;9382:24:17;;;;;;;;;9284:130::o;18339:1425::-;18432:7;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;18452::17::1;:16;:18::i;:::-;-1:-1:-1::0;;;;;18491:25:17;::::1;;::::0;;;:17:::1;:25;::::0;;;;;18481:40:::1;::::0;18491:25:::1;;18518:2;18481:9;:40::i;:::-;18532:14;:21:::0;;-1:-1:-1;;18532:21:17::1;;;::::0;;18599:4:::1;::::0;18532:21;;18587:17:::1;::::0;-1:-1:-1;;;;;18599:4:17::1;18587:11;:17::i;:::-;18566:38;;18615:29;18638:1;18625:10;:14;18641:2;18615:9;:29::i;:::-;18657:43;18685:6;18693;18657:27;:43::i;:::-;18713:24;18740:39;18760:6;18768:10;18740:19;:39::i;:::-;18713:66;;18790:35;18819:1;18800:16;:20;18822:2;18790:9;:35::i;:::-;18838:39;18858:6;18866:10;18838:19;:39::i;:::-;18888:45;18908:6;18916:16;18888:19;:45::i;:::-;18952:4;::::0;18946:43:::1;::::0;;-1:-1:-1;;;18946:43:17;;18971:4:::1;18946:43;::::0;::::1;::::0;;;;;;;;;-1:-1:-1;;;;;18952:4:17;;::::1;::::0;18946:16:::1;::::0;:43;;;;;18952:4:::1;::::0;18946:43;;;;;;;;18952:4;;18946:43;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;19328:4:17::1;::::0;19308:25:::1;::::0;-1:-1:-1;;;;;;19328:4:17::1;::::0;-1:-1:-1;19308:19:17::1;:25::i;:::-;19371:10;::::0;:56:::1;::::0;;-1:-1:-1;;;19371:56:17;;-1:-1:-1;;;;;19371:56:17;;::::1;;::::0;::::1;::::0;;;;;;;;;-1:-1:-1;;19371:10:17;;::::1;::::0;;::::1;::::0;:36:::1;::::0;:56;;;;;::::1;::::0;;;;;;;;;:10;:56;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;19371:56:17;;-1:-1:-1;19438:17:17::1;19458:58;19475:6:::0;19483:16;19371:56;19458:16:::1;:58::i;:::-;19438:78;;19527:28;19549:1;19537:9;:13;19552:2;19527:9;:28::i;:::-;19568:42;19581:6;19589:9;19600;19568:12;:42::i;:::-;19628:66;::::0;;-1:-1:-1;;;;;19628:66:17;;::::1;::::0;;;::::1;;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;;::::1;::::0;;;;;;;::::1;19707:14;:22:::0;;-1:-1:-1;;19707:22:17::1;::::0;;-1:-1:-1;;2622:22:64;19747:9:17;18339:1425;-1:-1:-1;;;;;;18339:1425:17:o;11107:122::-;11178:10;:8;:10::i;:::-;11199:9;:22;;-1:-1:-1;;;;;;11199:22:17;-1:-1:-1;;;;;11199:22:17;;;;;;;;;;11107:122::o;8701:575::-;8944:10;:8;:10::i;:::-;8976:13;;;8965:28;;8976:13;;;;8975:14;;8965:9;:28::i;:::-;9020:4;9004:20;;-1:-1:-1;;9004:20:17;;;;;9037:6;:16;;-1:-1:-1;;;;;;9037:16:17;;;-1:-1:-1;;;;;9037:16:17;;;;;;;9064:4;:12;;;;;;;;;;;;;;9087:9;:22;;;;;;;;;;;;;;;;9120:17;:38;;;;9169:17;:38;9218:23;:50;8701:575::o;1598:33::-;;;-1:-1:-1;;;;;1598:33:17;;:::o;1877:43::-;;;;:::o;10713:151::-;10797:10;:8;:10::i;:::-;10818:17;:38;;;;;;;-1:-1:-1;;10818:38:17;;;;;;;;;10713:151::o;17091:1240::-;17183:7;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;17203::17::1;:16;:18::i;:::-;-1:-1:-1::0;;;;;17242:25:17;::::1;;::::0;;;:17:::1;:25;::::0;;;;;17232:40:::1;::::0;17242:25:::1;;17269:2;17232:9;:40::i;:::-;17283:14;:21:::0;;-1:-1:-1;;17283:21:17::1;;;::::0;;;17339:19:::1;17351:6:::0;17339:11:::1;:19::i;:::-;17317:41;;17369:30;17393:1;17379:11;:15;17396:2;17369:9;:30::i;:::-;17412:43;17440:6;17448;17412:27;:43::i;:::-;17468:13;17484:19;17496:6;17484:11;:19::i;:::-;17468:35:::0;-1:-1:-1;17516:18:17::1;17537:43;-1:-1:-1::0;;;17537:22:17::1;:11:::0;17468:35;17537:15:::1;:22::i;:43::-;17642:4;::::0;17516:64;;-1:-1:-1;17604:43:17::1;::::0;17516:64;;17634:6;;-1:-1:-1;;;;;17642:4:17::1;17604:17;:43::i;:::-;17591:56;;17658:29;17681:1;17668:10;:14;17684:2;17658:9;:29::i;:::-;17725:10;::::0;:55:::1;::::0;;-1:-1:-1;;;17725:55:17;;-1:-1:-1;;;;;17725:55:17;;::::1;;::::0;::::1;::::0;;;;;;;;;-1:-1:-1;;17725:10:17;;::::1;::::0;;::::1;::::0;:35:::1;::::0;:55;;;;;::::1;::::0;;;;;;;;;:10;:55;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;17725:55:17;;-1:-1:-1;17791:23:17::1;17817:53;17834:6:::0;17842:11;17725:55;17817:16:::1;:53::i;:::-;17791:79:::0;-1:-1:-1;17881:18:17::1;17902:47;-1:-1:-1::0;;;17902:26:17::1;17791:79:::0;17922:5;17902:19:::1;:26::i;:47::-;18011:4;::::0;17881:68;;-1:-1:-1;17973:43:17::1;::::0;17881:68;;18003:6;;-1:-1:-1;;;;;18011:4:17::1;17973:17;:43::i;:::-;17960:56;;18029:39;18049:6;18057:10;18029:19;:39::i;:::-;18079:44;18099:6;18107:15;18079:19;:44::i;:::-;18142:4;::::0;18136:39:::1;::::0;;-1:-1:-1;;;18136:39:17;;-1:-1:-1;;;;;18136:39:17;;::::1;;::::0;::::1;::::0;;;;;;;;;18142:4;;;::::1;::::0;18136:16:::1;::::0;:39;;;;;18142:4:::1;::::0;18136:39;;;;;;;18142:4;;18136:39;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;-1:-1:-1::0;;18193:67:17::1;::::0;;-1:-1:-1;;;;;18193:67:17;;::::1;::::0;;;::::1;;::::0;::::1;::::0;;;;;;;;;;;;;;;;;;;;;::::1;::::0;-1:-1:-1;18193:67:17;;;;;;;-1:-1:-1;18193:67:17::1;18273:14;:22:::0;;-1:-1:-1;;18273:22:17::1;::::0;;-1:-1:-1;;2622:22:64;18313:10:17;17091:1240;-1:-1:-1;;;;;;;;17091:1240:17:o;32533:185::-;32643:9;;;32678:15;;32627:83;;;-1:-1:-1;;;32627:83:17;;-1:-1:-1;;;;;32627:83:17;;;;;;;;;;32600:7;32627:83;;;;;;32678:15;;;;32627:83;;;;;;32643:9;32695:14;;;;;;32627:83;;;;;;;;32600:7;;32643:9;;;;32627:35;;:83;;;;;;;;;;;;;;32643:9;32627:83;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32627:83:17;;32533:185;-1:-1:-1;;32533:185:17:o;25343:419::-;25553:7;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;25573:19:17::1;:17;:19::i;:::-;25603:25;25619:8;25603:15;:25::i;:::-;25646:108;25664:8;25674:16;25692:11;25705:16;25723:10;25735:7;25744:9;25646:17;:108::i;:::-;1660:1:64::0;2622:7;:22;25639:115:17;25343:419;-1:-1:-1;;;;;;;;25343:419:17:o;11427:147::-;11514:10;:8;:10::i;:::-;-1:-1:-1;;;;;11535:21:17;;;;;;;:13;:21;;;;;:31;11427:147::o;1004:42::-;1044:2;1004:42;:::o;3196:65::-;;;;;;;;;;;;;:::o;13114:533::-;13255:10;:8;:10::i;:::-;13276:60;1257:7;13286:16;:45;;13333:2;13276:9;:60::i;:::-;13347;1321:5;13357:18;:45;;13404:2;13347:9;:60::i;:::-;13418:66;1321:5;13428:24;:51;;13481:2;13418:9;:66::i;:::-;13495:15;:34;;;;13540:17;:38;13589:23;:50;13114:533::o;5604:61::-;;;;;;;;;;;;;:::o;3131:58::-;;;;;;;;;;;;;:::o;9422:137::-;9496:10;:8;:10::i;:::-;9517:15;:34;;-1:-1:-1;;;;;;9517:34:17;-1:-1:-1;;;;;9517:34:17;;;;;;;;;;9422:137::o;2649:42::-;;;;;;;;;:::o;19772:1748::-;19882:7;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;19912:13:17::1;::::0;19902:28:::1;::::0;19912:13:::1;::::0;::::1;;;19927:2;19902:9;:28::i;:::-;-1:-1:-1::0;;;;;19951:27:17;::::1;;::::0;;;:17:::1;:27;::::0;;;;;19941:42:::1;::::0;19951:27:::1;;19980:2;19941:9;:42::i;:::-;-1:-1:-1::0;;;;;20004:28:17;::::1;;::::0;;;:17:::1;:28;::::0;;;;;19994:43:::1;::::0;20004:28:::1;;20034:2;19994:9;:43::i;:::-;20048:36;20070:9;-1:-1:-1::0;;;;;20058:21:17::1;:8;-1:-1:-1::0;;;;;20058:21:17::1;;;20081:2;20048:9;:36::i;:::-;20097:14;:21:::0;;-1:-1:-1;;20097:21:17::1;;;::::0;;20131:47:::1;20159:8:::0;;20131:27:::1;:47::i;:::-;20189:49;20217:9;20228;20189:27;:49::i;:::-;20251:16;20270:21;20282:8;20270:11;:21::i;:::-;20251:40;;20302:27;20323:1;20312:8;:12;20326:2;20302:9;:27::i;:::-;20342:15;20360:21;20372:8;20360:11;:21::i;:::-;20342:39;;20392:16;20411:22;20423:9;20411:11;:22::i;:::-;20392:41:::0;-1:-1:-1;20446:17:17::1;20466:35;20392:41:::0;20466:21:::1;:8:::0;20479:7;20466:12:::1;:21::i;:35::-;20446:55;;20524:49;20542:9;20553:8;20563:9;20524:17;:49::i;:::-;20512:61:::0;-1:-1:-1;20678:18:17::1;20699:42;-1:-1:-1::0;;;20699:21:17::1;:8:::0;20712:7;20699:12:::1;:21::i;:42::-;20805:4;::::0;20678:63;;-1:-1:-1;20765:45:17::1;::::0;20678:63;;20795:8;;-1:-1:-1;;;;;20805:4:17::1;20765:17;:45::i;:::-;20848:10;::::0;:65:::1;::::0;;-1:-1:-1;;;20848:65:17;;-1:-1:-1;;;;;20848:65:17;;::::1;;::::0;::::1;::::0;;;::::1;::::0;;;;;;;;;;;;;;-1:-1:-1;;;20848:10:17;;;::::1;::::0;;::::1;::::0;:32:::1;::::0;:65;;;;;::::1;::::0;;;;;;;;;:10;:65;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;20848:65:17;;-1:-1:-1;20924:26:17::1;20953:54;20970:9:::0;20981;20848:65;20953:16:::1;:54::i;:::-;20924:83;;21020:41;21040:8;21050:10;21020:19;:41::i;:::-;21072:42;21092:9;21103:10;21072:19;:42::i;:::-;21127:39;21147:8;21157;21127:19;:39::i;:::-;21177:41;21197:9;21208;21177:19;:41::i;:::-;21231:32;21253:9;21231:21;:32::i;:::-;21276:54;21289:9;21300:18;21320:9;21276:12;:54::i;:::-;21348:93;::::0;;-1:-1:-1;;;;;21348:93:17;;::::1;::::0;;;;::::1;;::::0;::::1;::::0;;::::1;::::0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::1;::::0;;;;;;;::::1;21454:14;:22:::0;;-1:-1:-1;;21454:22:17::1;::::0;;-1:-1:-1;;2622:22:64;21494:18:17;19772:1748;-1:-1:-1;;;;;;;;;;19772:1748:17:o;890:50::-;-1:-1:-1;;;890:50:17;:::o;5748:64::-;;;;;;;;;;;;;:::o;2357:49::-;;;;:::o;34476:210::-;34556:7;34580:15;34576:34;;-1:-1:-1;34606:1:17;34599:8;;34576:34;34627:51;34638:6;34646:10;34658:19;34670:6;34658:11;:19::i;:::-;34627:10;:51::i;39362:693::-;-1:-1:-1;;;;;39519:29:17;;39484:7;39519:29;;;:16;:29;;;;;;;;;39582:24;:37;;;;;;39484:7;39651:25;;;:87;;39710:28;:10;39725:12;39710:14;:28::i;:::-;39651:87;;;39679:28;:12;39696:10;39679:16;:28::i;:::-;39630:108;-1:-1:-1;39749:13:17;39765:38;39790:12;39765:20;:4;39630:108;39765:8;:20::i;:38::-;39749:54;-1:-1:-1;39831:25:17;;;39814:14;39888:20;:4;39897:10;39888:8;:20::i;:::-;39869:39;;39919:15;39937:9;:53;;39971:19;:8;39984:5;39971:12;:19::i;:::-;39937:53;;;39949:19;:8;39962:5;39949:12;:19::i;:::-;39919:71;-1:-1:-1;40010:37:17;39919:71;40010:24;:10;40025:8;40010:14;:24::i;:37::-;40003:44;39362:693;-1:-1:-1;;;;;;;;;;;39362:693:17:o;2305:43::-;;;;;;:::o;2065:47::-;;;;:::o;34258:210::-;34338:7;34362:15;34358:34;;-1:-1:-1;34388:1:17;34381:8;;34358:34;34409:51;34420:6;34428:10;34440:19;34452:6;34440:11;:19::i;10067:145::-;10151:10;:8;:10::i;:::-;-1:-1:-1;;;;;10172:19:17;;;;;;;;:9;:19;;;;;:32;;-1:-1:-1;;10172:32:17;;;;;;;;;;10067:145::o;37077:581::-;37204:15;;-1:-1:-1;;;;;37175:24:17;;37151:7;37175:24;;;:16;:24;;;;;;37151:7;;37223:15;;37175:45;;:28;:45::i;:::-;:63;37171:82;;;-1:-1:-1;37249:1:17;37242:8;;37171:82;37335:15;;-1:-1:-1;;;;;37305:24:17;;37265:17;37305:24;;;:16;:24;;;;;;37265:17;;37285:66;;:45;;:15;;:19;:45::i;:66::-;-1:-1:-1;;;;;37383:19:17;;37362:18;37383:19;;;:11;:19;;;;;;37265:86;;-1:-1:-1;37417:15:17;37413:34;;37443:1;37436:8;;;;;;37413:34;-1:-1:-1;;;;;37488:20:17;;37459:26;37488:20;;;:12;:20;;;;;;;;:66;;37537:17;;37488:66;;;37511:23;;37488:66;-1:-1:-1;;;;;37595:23:17;;;;;;:15;:23;;;;;;37459:95;;-1:-1:-1;37572:78:17;;37639:10;;37572:62;;37624:9;;37572:47;;37459:95;;37572:22;:47::i;:::-;:51;;:62::i;:78::-;37565:85;37077:581;-1:-1:-1;;;;;37077:581:17:o;2565:34::-;;;;;;:::o;3592:57::-;;;;;;;;;;;;;:::o;3877:59::-;;;;;;;;;;;;;:::o;1765:48::-;;;;:::o;2606:34::-;;;;;;;;;:::o;33041:334::-;-1:-1:-1;;;;;33135:20:17;;33111:7;33135:20;;;:12;:20;;;;;;;;33131:79;;;-1:-1:-1;;;;;;33179:19:17;;;;;;:11;:19;;;;;;33172:26;;33131:79;-1:-1:-1;;;;;33263:21:17;;33220:18;33263:21;;;:13;:21;;;;;;33241:44;;33255:6;;33241:13;:44::i;:::-;-1:-1:-1;;;;;33343:23:17;;;;;;:15;:23;;;;;;;;;33318:11;:19;;;;;;;33220:65;;-1:-1:-1;33303:64:17;;:35;;33220:65;;33303:14;:35::i;:::-;:39;;:64::i;42163:216::-;42301:10;;:70;;;-1:-1:-1;;;42301:70:17;;-1:-1:-1;;;;;42301:70:17;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;42301:10:17;;;;;;;:30;;:70;;;;;;;;;;;;;;;:10;:70;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;42301:70:17;;42163:216;-1:-1:-1;;;;42163:216:17:o;40063:575::-;-1:-1:-1;;;;;40170:24:17;;40129:4;40170:24;;;:16;:24;;;;;;40129:4;;40209:9;40205:37;;40230:5;40237:1;40222:17;;;;;;;40205:37;40254:17;40274:19;40286:6;40274:11;:19::i;:::-;-1:-1:-1;;;;;40327:32:17;;40304:20;40327:32;;;:24;:32;;;;;;40254:39;;-1:-1:-1;40391:24:17;;;:84;;40448:27;:9;40462:12;40448:13;:27::i;:::-;40391:84;;;40418:27;:12;40435:9;40418:16;:27::i;:::-;40370:105;-1:-1:-1;40486:13:17;40502:38;40527:12;40502:20;:4;40370:105;40502:8;:20::i;:38::-;40568:24;;;;;-1:-1:-1;40486:54:17;;-1:-1:-1;;;;40063:575:17;;;;:::o;4297:60::-;;;;;;;;;;;;;:::o;2413:41::-;;;;:::o;5162:67::-;;;;;;;;;;;;;:::o;43772:289::-;43959:10;;:94;;;-1:-1:-1;;;43959:94:17;;-1:-1:-1;;;;;43959:94:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;43959:10:17;;;;;;;:28;;:94;;;;;;;;;;;;;;;:10;:94;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;43959:94:17;;43772:289;-1:-1:-1;;;;;;43772:289:17:o;16452:170::-;16546:10;:8;:10::i;:::-;16567:47;-1:-1:-1;;;;;16567:27:17;;16595:9;16606:7;16567:27;:47::i;:::-;16452:170;;;:::o;11010:89::-;11060:10;:8;:10::i;:::-;11081:3;:10;;-1:-1:-1;;;;;;11081:10:17;-1:-1:-1;;;;;11081:10:17;;;;;;;;;;11010:89::o;10872:130::-;10947:10;:8;:10::i;:::-;10968:11;:26;10872:130::o;11237:182::-;11312:10;:8;:10::i;:::-;11333:41;986:5;11343:12;:27;11372:1;11333:9;:41::i;:::-;11385:11;:26;11237:182::o;32050:283::-;32237:10;;:88;;;-1:-1:-1;;;32237:88:17;;-1:-1:-1;;;;;32237:88:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;32237:10:17;;;;;;;;:30;;:88;;;;;;;;;;;;:10;:88;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;32237:88:17;;;;;;;;;;;-1:-1:-1;32050:283:17;-1:-1:-1;;;;;;32050:283:17:o;16015:357::-;16100:10;:8;:10::i;:::-;-1:-1:-1;;;;;16144:19:17;;16123:18;16144:19;;;:11;:19;;;;;;16178:20;;;16174:126;;;16215:52;16235:6;16243:23;:7;16255:10;16243:11;:23::i;:::-;16215:19;:52::i;:::-;16282:7;;;16174:126;16312:52;16332:6;16340:23;:10;16355:7;16340:14;:23::i;:::-;16312:19;:52::i;16015:357::-;;;:::o;5314:61::-;;;;;;;;;;;;;:::o;2261:37::-;;;;:::o;42387:300::-;42579:10;;:100;;;-1:-1:-1;;;42579:100:17;;-1:-1:-1;;;;;42579:100:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;42579:10:17;;;;;;;:24;;:100;;;;;;;;;;;;;;;:10;:100;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;42579:100:17;;42387:300;-1:-1:-1;;;;;;;42387:300:17:o;3065:59::-;;;;;;;;;;;;;;;:::o;3329:57::-;;;;;;;;;;;;;;;:::o;38524:666::-;38715:7;38736:14;38752:13;38769:72;38778:11;38791:5;38798:13;38813:7;38822:18;38769:8;:72::i;:::-;38735:106;;-1:-1:-1;38735:106:17;-1:-1:-1;38852:16:17;38871:21;:5;38881:10;38871:9;:21::i;:::-;38852:40;;38903:15;38933:7;38929:199;;;38967:9;:53;;39001:19;:8;39014:5;39001:12;:19::i;:::-;38967:53;;;38979:19;:8;38992:5;38979:12;:19::i;:::-;38957:63;;38929:199;;;39063:9;:53;;39097:19;:8;39110:5;39097:12;:19::i;:::-;39063:53;;;39075:19;:8;39088:5;39075:12;:19::i;:::-;39053:63;;38929:199;39145:37;39174:7;39145:24;:10;39160:8;39145:14;:24::i;:37::-;39138:44;38524:666;-1:-1:-1;;;;;;;;;;;;38524:666:17:o;2515:41::-;;;;:::o;29204:2788::-;1704:1:64;2310:7;;:19;;2302:63;;;;;-1:-1:-1;;;2302:63:64;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2302:63:64;;;;;;;;;;;;;;;1704:1;2443:7;:18;29378:24:17::1;::::0;;;::::1;;;29374:96;;;29442:10;29429:24;::::0;;;:12:::1;:24;::::0;;;;;29419:39:::1;::::0;29429:24:::1;;29455:2;29419:9;:39::i;:::-;29559:15;:23:::0;;-1:-1:-1;;29559:23:17::1;::::0;;29595:58:::1;29623:16:::0;29641:11;29595:27:::1;:58::i;:::-;29666:11;29680:64;29695:8;29705:16;29723:11;29736:7;29680:14;:64::i;:::-;29666:78;;29755:24;;:::i;:::-;-1:-1:-1::0;29782:14:17::1;::::0;;;:9:::1;:14;::::0;;;;;;;;29755:41;;::::1;::::0;::::1;::::0;;;;;;;::::1;::::0;::::1;::::0;;;::::1;::::0;;;;::::1;::::0;::::1;::::0;;;;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;::::1;::::0;;;;;::::1;;::::0;;;;;29807:32:::1;::::0;29817:17;;29836:2:::1;29807:9;:32::i;:::-;29853:24;29879:18:::0;29901:76:::1;29921:8;29931:16;29949:11;29962:7;29971:5;29901:19;:76::i;:::-;29852:125;;;;29988:36;29998:16;30018:1;29998:21;;30021:2;29988:9;:36::i;:::-;30039:16;30059:1;30039:21;30035:330;;;30200:95;30218:8;30228:16;30246:11;30259:1;30262:8;:13;;;30277:7;30286:8;30200:17;:95::i;:::-;-1:-1:-1::0;;30310:15:17::1;:22:::0;;-1:-1:-1;;30310:22:17::1;30328:4;30310:22;::::0;;-1:-1:-1;30347:7:17::1;::::0;-1:-1:-1;;30347:7:17::1;30035:330;30377:17;30397:43;30411:16;30429:10;30397:13;:43::i;:::-;-1:-1:-1::0;;;;;30483:29:17;::::1;;::::0;;;:11:::1;:29;::::0;;;;;30377:63;;-1:-1:-1;30483:44:17::1;::::0;30377:63;30483:33:::1;:44::i;:::-;-1:-1:-1::0;;;;;30451:29:17;::::1;;::::0;;;:11:::1;:29;::::0;;;;;;;;:76;;;;30543:58;;;;;;;::::1;::::0;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;30543:58:17;;;;;;;::::1;30614:65;30638:16;30656:8;:22;;;30614:23;:65::i;:::-;30694:7;30690:217;;;30777:19;::::0;::::1;::::0;30759:13;;30718:80:::1;::::0;30741:16;;30759:38:::1;::::0;:17:::1;:38::i;:::-;30718:22;:80::i;:::-;30813:82;30833:16;30851:43;30865:16;30883:10;30851:13;:43::i;30813:82::-;30919:17;30939:7;:61;;30976:24;30988:11;30976;:24::i;:::-;30939:61;;;30949:24;30961:11;30949;:24::i;:::-;30919:81;;31016:165;31034:3;31039:8;31049:16;31067:11;31080:7;31089:8;:13;;;31104:8;:19;;;31125:8;:22;;;31149:8;:20;;;31171:9;31016:165;;;;;;;;;-1:-1:-1::0;;;;;31016:165:17::1;;;;;;-1:-1:-1::0;;;;;31016:165:17::1;;;;;;-1:-1:-1::0;;;;;31016:165:17::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;31199:7;31198:8;:44;;;;;31223:8;:19;;;31210:10;:32;31198:44;31194:248;;;31289:19;::::0;::::1;::::0;31259:27:::1;::::0;31289:35:::1;::::0;31313:10;31289:23:::1;:35::i;:::-;31259:65;;31339:91;31359:16;31377:52;31391:16;31409:19;31377:13;:52::i;:::-;31339:19;:91::i;:::-;31194:248;;31459:7;31454:93;;31483:52;31508:11;31521:8;:13;;;31483:24;:52::i;:::-;31566:14;::::0;;;:9:::1;:14;::::0;;;;31559:21;;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;::::1;;::::0;31823:17:::1;::::0;31753:89:::1;::::0;31773:16;;31791:50:::1;::::0;31773:16;;31791:13:::1;:50::i;31753:89::-;31853:96;31866:16;31884:50;31898:16;31916:17;;31884:13;:50::i;:::-;31936:12;31853;:96::i;:::-;-1:-1:-1::0;;31962:15:17::1;:22:::0;;-1:-1:-1;;31962:22:17::1;31980:4;31962:22;::::0;;-1:-1:-1;;;;2474:1:64::1;-1:-1:-1::0;;1660:1:64;2622:7;:22;-1:-1:-1;;;29204:2788:17:o;2127:52::-;;;;:::o;32341:184::-;32451:9;;;32485:15;;32435:82;;;-1:-1:-1;;;32435:82:17;;-1:-1:-1;;;;;32435:82:17;;;;;;;;;;32451:9;32435:82;;;;32485:15;;;;32435:82;;;;;;32451:9;32502:14;;;;;;32435:82;;;;;;;32408:7;;32451:9;;;;32435:35;;:82;;;;;;;;;;;;;;;32451:9;32435:82;;;;;;;;;;3010:46;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3010:46:17;;-1:-1:-1;3010:46:17;:::o;14896:553::-;14958:10;:8;:10::i;:::-;-1:-1:-1;;;;;14989:25:17;;;;;;:17;:25;;;;;;14979:40;;14989:25;;15016:2;14979:9;:40::i;:::-;-1:-1:-1;;;;;15072:20:17;;;;;;:12;:20;;;;;;15050:17;;:43;;:21;:43::i;:::-;15030:17;:63;-1:-1:-1;;;;;15111:25:17;;;;;;:17;:25;;;;;;;;15104:32;;-1:-1:-1;;15104:32:17;;;;;;15154:13;:21;;;;;15147:28;;;15193:12;:20;;;;;15186:27;;;15231:20;:28;;;;;15224:35;;;15277:14;:22;;;;;15270:29;;;15317:12;:20;;;;;15310:27;;;;;;15355:23;;;;;;15348:30;;;;;;;15413:21;;:28;;15104:32;15413:25;:28::i;:::-;15389:21;:52;-1:-1:-1;14896:553:17:o;11582:159::-;11675:10;:8;:10::i;:::-;-1:-1:-1;;;;;11696:27:17;;;;;;;:19;:27;;;;;:37;11582:159::o;5018:58::-;;;;;;;;;;;;;:::o;10220:179::-;10318:10;:8;:10::i;:::-;10339:24;:52;;;;;;;-1:-1:-1;;10339:52:17;;;;;;;;;10220:179::o;15457:316::-;15541:7;15561:10;:8;:10::i;:::-;-1:-1:-1;;;;;15599:19:17;;15582:14;15599:19;;;:11;:19;;;;;;15632:11;15629:29;;15654:1;15647:8;;;;;15629:29;-1:-1:-1;;;;;15668:19:17;;15690:1;15668:19;;;:11;:19;;;;;:23;15702:39;15680:6;15723;15731:9;15702:12;:39::i;2950:51::-;;;;;;;;;;;;;;;:::o;1640:28::-;;;-1:-1:-1;;;;;1640:28:17;;:::o;1561:30::-;;;-1:-1:-1;;;;;1561:30:17;;:::o;34694:265::-;34787:7;34811:15;34807:34;;-1:-1:-1;34837:1:17;34830:8;;34807:34;-1:-1:-1;;;;;34870:21:17;;34851:16;34870:21;;;:13;:21;;;;;;34909:42;34944:6;34909:30;:10;34924:2;:14;;;34909;:30::i;36103:966::-;36225:10;;:69;;;-1:-1:-1;;;36225:69:17;;-1:-1:-1;;;;;36225:69:17;;;;;;;;;;;;;;;;-1:-1:-1;;36225:10:17;;;;;;;:38;;:69;;;;;;;;;;;;;;;-1:-1:-1;36225:10:17;:69;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;36225:69:17;;-1:-1:-1;36225:69:17;36305:52;;36339:7;;;36305:52;-1:-1:-1;;;;;36373:34:17;;;;;;:16;:34;;;;;;36369:187;;36507:15;;36466:57;;:36;:15;36507;36466:19;:36::i;:57::-;-1:-1:-1;;;;;36429:34:17;;;;;;:16;:34;;;;;:94;-1:-1:-1;36538:7:17;;36369:187;36611:15;;-1:-1:-1;;;;;36572:34:17;;;;;;:16;:34;;;;;;36630:15;;36572:55;;:34;:38;:55::i;:::-;:73;36568:112;;;36662:7;;;36568:112;36692:19;36714:36;36733:16;36714:18;:36::i;:::-;-1:-1:-1;;;;;36804:40:17;;;;;;:22;:40;;;;;;36692:58;;-1:-1:-1;36804:57:17;;36692:58;36804:44;:57::i;:::-;-1:-1:-1;;;;;36761:40:17;;;;;;:22;:40;;;;;:100;36950:15;;36909:57;;:36;:15;36950;36909:19;:36::i;:57::-;-1:-1:-1;;;;;36872:34:17;;;;;;:16;:34;;;;;;;;:94;;;;37020:22;:40;;;;;;;36984:77;;;;;;;;;;;;;;;;;;;;;;36103:966;;;;:::o;1205:59::-;1257:7;1205:59;:::o;42695:266::-;42866:10;;:87;;;-1:-1:-1;;;42866:87:17;;-1:-1:-1;;;;;42866:87:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;42866:10:17;;;;;;;:25;;:87;;;;;;;;;;;;;;;:10;:87;;;;;;;;;;5821:41;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5821:41:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;2256:471:52:-;2314:7;2559:6;2555:47;;-1:-1:-1;2589:1:52;2582:8;;2555:47;2626:5;;;2630:1;2626;:5;:1;2650:5;;;;;:10;2642:56;;;;-1:-1:-1;;;2642:56:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3203:132;3261:7;3288:39;3292:1;3295;3288:39;;;;;;;;;;;;;-1:-1:-1;;;3288:39:52;;;:3;:39::i;54162:84:17:-;54230:3;;54206:32;;-1:-1:-1;;;;;54230:3:17;54216:10;:17;54235:2;54206:9;:32::i;:::-;54162:84::o;902:181:52:-;960:7;992:5;;;1016:6;;;;1008:46;;;;;-1:-1:-1;;;1008:46:52;;;;;;;;;;;;-1:-1:-1;;;1008:46:52;;;;;;;;;;;;;;1366:136;1424:7;1451:43;1455:1;1458;1451:43;;;;;;;;;;;;;;;;;:3;:43::i;54740:127:17:-;54840:18;;;;:6;:18;;;;;54828:10;54820:39;;;;-1:-1:-1;;;54820:39:17;;;;;;;;;;;;-1:-1:-1;;54820:39:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;54587:145;54644:11;;54640:33;;54664:7;;54640:33;54683:41;54708:11;;54693;:26;;54721:2;54683:9;:41::i;47896:223::-;47967:10;-1:-1:-1;;;;;47967:22:17;;;47963:39;;;47993:7;;47963:39;48030:6;;-1:-1:-1;;;;;48030:6:17;48016:10;:20;48012:37;;;48040:7;;48012:37;-1:-1:-1;;;;;48069:25:17;;;;;;:15;:25;;;;;;;;48095:10;48069:37;;;;;;;;48059:52;;48069:37;;48108:2;48059:9;:52::i;:::-;47896:223;:::o;48127:575::-;48241:7;48237:233;;;48265:46;48295:11;-1:-1:-1;;;;;48275:31:17;:16;-1:-1:-1;;;;;48275:31:17;;48308:2;48265:9;:46::i;:::-;-1:-1:-1;;;;;48336:35:17;;;;;;:17;:35;;;;;;48326:50;;48336:35;;48373:2;48326:9;:50::i;:::-;-1:-1:-1;;;;;48402:30:17;;;;;;:12;:30;;;;;;48391:46;;48402:30;;48401:31;48434:2;48391:9;:46::i;:::-;48452:7;;48237:233;-1:-1:-1;;;;;48492:35:17;;;;;;:17;:35;;;;;;48482:50;;48492:35;;48529:2;48482:9;:50::i;:::-;-1:-1:-1;;;;;48553:30:17;;;;;;:12;:30;;;;;;48543:45;;48553:30;;48585:2;48543:9;:45::i;:::-;-1:-1:-1;;;;;48610:25:17;;;;;;:12;:25;;;;;;48599:41;;48610:25;;48609:26;48637:2;48599:9;:41::i;:::-;-1:-1:-1;;;;;48661:28:17;;;;;;:15;:28;;;;;;48651:43;;48661:28;;48691:2;48651:9;:43::i;49193:727::-;49379:7;49399:14;49416:76;49431:8;49441:16;49459:11;49472:7;49481:10;49416:14;:76::i;:::-;49399:93;;49505:18;49526:89;49540:8;49550:16;49568:11;49581:7;49590:5;49597:17;49526:13;:89::i;:::-;49505:110;-1:-1:-1;49635:22:17;:6;49505:110;49635:10;:22::i;:::-;49626:31;;49670:17;49690:39;49704:16;49722:6;49690:13;:39::i;:::-;-1:-1:-1;;;;;49772:29:17;;;;;;:11;:29;;;;;;49670:59;;-1:-1:-1;49772:44:17;;49670:59;49772:33;:44::i;:::-;-1:-1:-1;;;;;49740:29:17;;;;;;:11;:29;;;;;;;;;:76;;;;49834:54;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;49834:54:17;;;;;;;;-1:-1:-1;49906:6:17;;49193:727;-1:-1:-1;;;;;;;;;49193:727:17:o;49928:291::-;-1:-1:-1;;;;;50024:21:17;;49982:7;50024:21;;;:13;:21;;;;;;;;;50078:39;;-1:-1:-1;;;50078:39:17;;50111:4;50078:39;;;;;;49982:7;;50024:21;;49982:7;;50024:21;;50078:24;;:39;;;;;50024:21;;50078:39;;;;;50024:21;50078:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;50078:39:17;-1:-1:-1;;;;;50128:21:17;;;;;;:13;50078:39;50128:21;;;;:35;;;50078:39;-1:-1:-1;50183:28:17;50078:39;50199:11;50183:15;:28::i;47653:235::-;47744:10;47740:95;;47771:31;47781:16;;47799:2;47771:9;:31::i;:::-;47817:7;;47740:95;47845:35;47864:11;47855:5;:20;;47877:2;47845:9;:35::i;52435:282::-;-1:-1:-1;;;;;52546:23:17;;;;;;:15;:23;;;;;;:36;;52574:7;52546:27;:36::i;:::-;-1:-1:-1;;;;;52520:23:17;;;;;;:15;:23;;;;;;;;:62;;;52630:11;:19;;;;;;52603:23;;52593:61;;52603:46;;52651:2;52593:9;:61::i;:::-;52670:39;;;-1:-1:-1;;;;;52670:39:17;;;;;;;;;;;;;;;;;;;;;;;52435:282;;:::o;52974:213::-;-1:-1:-1;;;;;53085:21:17;;;;;;:13;:21;;;;;;:37;;53111:10;53085:25;:37::i;:::-;-1:-1:-1;;;;;53061:21:17;;;;;;:13;:21;;;;;;;;;:61;;;;53138:41;;;;;;;;;;;;;;;;;;;;;;;;52974:213;;:::o;53195:::-;-1:-1:-1;;;;;53306:21:17;;;;;;:13;:21;;;;;;:37;;53332:10;53306:25;:37::i;:::-;-1:-1:-1;;;;;53282:21:17;;;;;;:13;:21;;;;;;;;;:61;;;;53359:41;;;;;;;;;;;;;;;;;;;;;;;;53195:213;;:::o;50644:318::-;-1:-1:-1;;;;;50747:19:17;;;;;;:11;:19;;;;;;:32;;50771:7;50747:23;:32::i;:::-;-1:-1:-1;;;;;50725:19:17;;;;;;:11;:19;;;;;;;;:54;;;;50808:39;;-1:-1:-1;;;50808:39:17;;50841:4;50808:39;;;;;;50725:19;;50808:24;;:39;;;;;50725:19;50808:39;;;;;50725:19;50808:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;50808:39:17;-1:-1:-1;;;;;50868:19:17;;;;;;:11;50808:39;50868:19;;;;;50808:39;;-1:-1:-1;50858:45:17;;50868:30;-1:-1:-1;50868:30:17;50900:2;50858:9;:45::i;:::-;50919:35;;;-1:-1:-1;;;;;50919:35:17;;;;;;;;;;;;;;;;;;;;;;;50644:318;;;:::o;50970:296::-;51073:62;;;;;;;;;;;-1:-1:-1;;;51073:62:17;;;;;;;;-1:-1:-1;;;;;51073:19:17;;-1:-1:-1;51073:19:17;;;:11;:19;;;;;;;;:62;;51097:7;;51073:23;:62::i;:::-;-1:-1:-1;;;;;51051:19:17;;;;;;:11;:19;;;;;;;;:84;;;51156:15;:23;;;;;;51146:61;;-1:-1:-1;51156:46:17;51204:2;51146:9;:61::i;:::-;51223:35;;;-1:-1:-1;;;;;51223:35:17;;;;;;;;;;;;;;;;;;;;;;;50970:296;;:::o;53416:346::-;-1:-1:-1;;;;;53530:24:17;;;;;;:16;:24;;;;;;:37;;53559:7;53530:28;:37::i;:::-;-1:-1:-1;;;;;53503:24:17;;;;;;:16;:24;;;;;;;;:64;;;;53598:19;:27;;;;53640:12;;53636:119;;-1:-1:-1;;;;;53677:24:17;;;;;;:16;:24;;;;;;:35;-1:-1:-1;53677:35:17;53669:74;;;;;-1:-1:-1;;;53669:74:17;;;;;;;;;;;;-1:-1:-1;;;53669:74:17;;;;;;;;;;;;;;54346:141;54402:13;;;;;;;54398:82;;;54452:10;54442:21;;;;:9;:21;;;;;;54432:36;;54442:21;;54465:2;54432:9;:36::i;51843:584::-;-1:-1:-1;;;;;51940:19:17;;51924:13;51940:19;;;:11;:19;;;;;;52176:16;;;52172:146;;-1:-1:-1;;;;;52209:19:17;;52231:1;52209:19;;;:11;:19;;;;;;;;:23;;;;52252:33;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;52252:33:17;;;;;;;;;52300:7;;;52172:146;52350:18;:5;52360:7;52350:9;:18::i;:::-;-1:-1:-1;;;;;52328:19:17;;;;;;:11;:19;;;;;;;;;:40;;;;52384:35;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;52384:35:17;;;;;;;;;;51843:584;;;:::o;50457:179::-;50521:19;50550:6;-1:-1:-1;;;;;50543:24:17;;50576:4;50543:39;;;;;;;;;;;;;-1:-1:-1;;;;;50543:39:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;50543:39:17;-1:-1:-1;;;;;50593:21:17;;;;;;;:13;50543:39;50593:21;;;;:35;;;;-1:-1:-1;50457:179:17:o;48710:475::-;48811:7;;48856:80;815:5;48856:54;48868:41;815:5;48893:15;48868:24;:41::i;:::-;48856:7;;:11;:54::i;:80::-;48831:105;-1:-1:-1;48947:17:17;48967:27;:7;48831:105;48967:11;:27::i;:::-;-1:-1:-1;;;;;49027:19:17;;;;;;:11;:19;;;;;;48947:47;;-1:-1:-1;49027:34:17;;48947:47;49027:23;:34::i;:::-;-1:-1:-1;;;;;49005:19:17;;;;;;:11;:19;;;;;:56;49077:68;49017:6;49101:32;49017:6;49123:9;49101:13;:32::i;:::-;49077:68;;;-1:-1:-1;;;;;49077:68:17;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;49163:14:17;48710:475;-1:-1:-1;;;;48710:475:17:o;50227:222::-;50320:47;-1:-1:-1;;;;;50320:27:17;;50348:9;50359:7;50320:27;:47::i;:::-;50402:39;;;-1:-1:-1;;;50402:39:17;;50435:4;50402:39;;;;;;-1:-1:-1;;;;;50402:24:17;;;;;:39;;;;;;;;;;;;;;:24;:39;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;50402:39:17;-1:-1:-1;;;;;50378:21:17;;;;;;;:13;50402:39;50378:21;;;;:63;;;;-1:-1:-1;;50227:222:17:o;51472:363::-;-1:-1:-1;;;;;51575:19:17;;;;;;:11;:19;;;;;;:32;;51599:7;51575:23;:32::i;:::-;-1:-1:-1;;;;;51553:19:17;;;;;;:11;:19;;;;;;;;:54;;;;51642:14;:22;;;;51679:18;;51675:102;;-1:-1:-1;;;;;51724:19:17;;;;;;:11;:19;;;;;;51714:51;;51724:36;-1:-1:-1;51724:36:17;51762:2;51714:9;:51::i;:::-;51792:35;;;-1:-1:-1;;;;;51792:35:17;;;;;;;;;;;;;;;;;;;;;;;51472:363;;;:::o;25770:3426::-;25978:10;;:126;;;-1:-1:-1;;;25978:126:17;;-1:-1:-1;;;;;25978:126:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;25978:10:17;;;;;;;:35;;:126;;;;;-1:-1:-1;;25978:126:17;;;;;;;;:10;:126;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26115:58;26143:16;26161:11;26115:27;:58::i;:::-;26186:11;26200:64;26215:8;26225:16;26243:11;26256:7;26200:14;:64::i;:::-;26275:25;26303:14;;;:9;:14;;;;;26338:13;;26186:78;;-1:-1:-1;26303:14:17;26328:32;;26338:17;;26357:2;26328:9;:32::i;:::-;26371:42;26398:10;26381:8;:13;;;:27;;26410:2;26371:9;:42::i;:::-;26424:54;26457:16;26434:8;:19;;;:39;;26475:2;26424:9;:54::i;:::-;26512:19;;;;26678:13;;26635:22;;;;26491:18;;26635:57;;:38;;26662:10;26635:26;:38::i;:57::-;26728:22;;;;26612:80;;-1:-1:-1;26728:40:17;;26612:80;26728:26;:40::i;:::-;26703:22;;;:65;26779:55;26803:16;26821:12;26779:23;:55::i;:::-;25770:3426;26859:14;26875:22;26901:97;26919:8;26929:16;26947:11;26960:16;26978:10;26990:7;26901:17;:97::i;:::-;27015:13;;26858:140;;-1:-1:-1;26858:140:17;-1:-1:-1;27015:27:17;;27011:1668;;27087:59;27107:16;27125:11;27138:7;27087:19;:59::i;:::-;27059:25;;;:87;27177:13;;:29;;27195:10;27177:17;:29::i;:::-;27161:45;;;27256:19;;;;27223:53;;27161:45;27223:17;:53::i;:::-;27291:75;27311:8;27321:16;27339:11;27352:7;27361:4;27291:19;:75::i;:::-;;;27387:7;27383:196;;;27415:77;27438:16;27456:35;27471:8;:19;;;27456:10;:14;;:35;;;;:::i;27415:77::-;27511:52;27534:16;27552:10;27511:22;:52::i;:::-;27595:13;27611:7;:61;;27648:24;27660:11;27648;:24::i;:::-;27611:61;;;27621:24;27633:11;27621;:24::i;:::-;27595:77;-1:-1:-1;;;;;;;;;;;;27709:3:17;27714:8;27724:16;27742:11;27755:16;27773:10;27785:7;27595:77;27801:26;:6;27812:14;27801:10;:26::i;:::-;27692:136;;;;;;-1:-1:-1;;;;;27692:136:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27868:13;;27883:19;;;;27904:21;;;;27927:25;;;;27954:22;;;;27978:20;;;;27848:158;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;27848:158:17;;;;;;;;27011:1668;;;;28043:7;28039:171;;;28071:52;28094:16;28112:10;28071:22;:52::i;:::-;28142;28165:16;28183:10;28142:22;:52::i;:::-;28226:13;28242:7;:61;;28279:24;28291:11;28279;:24::i;:::-;28242:61;;;28252:24;28264:11;28252;:24::i;:::-;28226:77;-1:-1:-1;;;;;;;;;;;;28340:3:17;28345:8;28355:16;28373:11;28386:16;28404:10;28416:7;28226:77;28432:26;:6;28443:14;28432:10;:26::i;:::-;28323:136;;;;;;-1:-1:-1;;;;;28323:136:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28498:13;;28513:19;;;;28534:21;;;;28557:25;;;;28584:22;;;;28608:20;;;;28479:150;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;28653:14:17;;;;:9;:14;;;;;28646:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27011:1668;28696:7;28691:90;;28720:49;28745:11;28758:10;28720:24;:49::i;:::-;28797:10;;28793:375;;28828:7;28824:126;;;28856:78;28876:16;28894:39;28908:16;28926:6;28894:13;:39::i;28856:78::-;28964:26;28993:47;29007:16;29025:14;28993:13;:47::i;:::-;28964:76;;29055:61;29068:16;29086:18;29106:9;29055:12;:61::i;:::-;29138:18;-1:-1:-1;29131:25:17;;-1:-1:-1;;;;;29131:25:17;28793:375;-1:-1:-1;29187:1:17;;25770:3426;-1:-1:-1;;;;;;;;;;;;25770:3426:17:o;51274:190::-;-1:-1:-1;;;;;51371:21:17;;;;;;:13;:21;;;;;;;;;51349:11;:19;;;;;;;:43;51345:112;;;51409:36;;;-1:-1:-1;;;51409:36:17;;;;;;;;;;;;-1:-1:-1;;;51409:36:17;;;;;;;;;;;;;;711:177:60;821:58;;;-1:-1:-1;;;;;821:58:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;821:58:60;-1:-1:-1;;;821:58:60;;;794:86;;814:5;;794:19;:86::i;52725:241:17:-;52836:67;;;;;;;;;;;-1:-1:-1;;;52836:67:17;;;;;;;;-1:-1:-1;;;;;52836:23:17;;-1:-1:-1;52836:23:17;;;:15;:23;;;;;;;;:67;;52864:7;;52836:27;:67::i;:::-;-1:-1:-1;;;;;52810:23:17;;;;;;:15;:23;;;;;;;;;:93;;;;52919:39;;;;;;;;;;;;;;;;;;;;;;;;52725:241;;:::o;53770:292::-;-1:-1:-1;;;;;53871:24:17;;53856:12;53871:24;;;:16;:24;;;;;;53910:14;;;53906:92;;;-1:-1:-1;;;;;;53939:24:17;;53966:1;53939:24;;;:16;:24;;;;;:28;53980:7;;53906:92;54037:17;:4;54046:7;54037:8;:17::i;:::-;-1:-1:-1;;;;;54010:24:17;;;;;;:16;:24;;;;;:44;-1:-1:-1;53770:292:17;;:::o;3831:278:52:-;3917:7;3952:12;3945:5;3937:28;;;;-1:-1:-1;;;3937:28:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3976:9;3992:1;3988;:5;;;;;;;3831:278;-1:-1:-1;;;;;3831:278:52:o;1805:192::-;1891:7;1927:12;1919:6;;;;1911:29;;;;-1:-1:-1;;;1911:29:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1963:5:52;;;1805:192::o;44370:3275:17:-;44539:7;44548;44568:11;44582:64;44597:8;44607:16;44625:11;44638:7;44582:14;:64::i;:::-;44657:25;44685:14;;;:9;:14;;;;;44807:13;;44822:25;;;;44568:78;;-1:-1:-1;44685:14:17;;44726:122;;44745:8;;44755:16;;44773:11;;44786:7;;44795:10;;44726:18;:122::i;:::-;44712:136;;44859:14;44884:21;44989:15;45006:13;45023:96;45032:11;45045:8;:13;;;45060:8;:21;;;45083:7;45092:8;:26;;;45023:8;:96::i;:::-;45252:13;;44988:131;;-1:-1:-1;44988:131:17;;-1:-1:-1;44988:131:17;-1:-1:-1;45226:40:17;;:21;:10;44988:131;45226:14;:21::i;:40::-;45210:56;;44370:3275;;45290:14;45352:9;:30;;;;;45381:1;45365:13;:17;45352:30;45348:451;;;-1:-1:-1;45459:20:17;;;;;:44;;45436:67;;45408:13;45607:7;45602:186;;45635:19;45657:46;45671:16;45689:13;45657;:46::i;:::-;45635:68;;45722:50;45742:16;45760:11;45722:19;:50::i;:::-;45602:186;;45816:9;45815:10;:31;;;;;45845:1;45829:13;:17;45815:31;45811:647;;;45885:19;;;;:38;;45909:13;45885:23;:38::i;:::-;45863:19;;;:60;46182:7;46177:186;;46210:19;46232:46;46246:16;46264:13;46232;:46::i;:::-;46210:68;;46297:50;46317:16;46335:11;46297:19;:50::i;:::-;46177:186;;46402:20;;;;;:44;;;46379:67;;45811:647;46581:20;;46577:168;;46627:28;:6;46638:16;46627:10;:28::i;:::-;46692:19;;;;46618:37;;-1:-1:-1;46692:41:17;;46716:16;46692:23;:41::i;:::-;46670:19;;;:63;46577:168;46848:13;;:27;;46844:138;;;46912:19;;;;46901:31;;:6;;:10;:31::i;:::-;46969:1;46947:19;;;:23;46892:40;-1:-1:-1;46844:138:17;47174:6;47195:12;;;47191:345;;;47241:15;:6;47252:3;47241:10;:15::i;:::-;47224:32;;47191:345;;;47311:19;;;;:28;;47335:3;47311:23;:28::i;:::-;47289:19;;;:50;47354:171;;;;47386:17;47406:36;47420:16;47438:3;47406:13;:36::i;:::-;47386:56;;47461:48;47481:16;47499:9;47461:19;:48::i;:::-;47354:171;;47553:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;47614:6;;;;-1:-1:-1;44370:3275:17;-1:-1:-1;;;;;;;;;;;;44370:3275:17:o;3016:761:60:-;3440:23;3466:69;3494:4;3466:69;;;;;;;;;;;;;;;;;3474:5;-1:-1:-1;;;;;3466:27:60;;;:69;;;;;:::i;:::-;3550:17;;3440:95;;-1:-1:-1;3550:21:60;3546:224;;3692:10;3681:30;;;;;;;;;;;;;;;-1:-1:-1;3681:30:60;3673:85;;;;-1:-1:-1;;;3673:85:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3661:195:61;3764:12;3796:52;3818:6;3826:4;3832:1;3835:12;3764;4965:18;4976:6;4965:10;:18::i;:::-;4957:60;;;;;-1:-1:-1;;;4957:60:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;5091:12;5105:23;5132:6;-1:-1:-1;;;;;5132:11:61;5152:5;5160:4;5132:33;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5132:33:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5090:75;;;;5183:52;5201:7;5210:10;5222:12;5183:17;:52::i;743:422::-;1110:20;1149:8;;;743:422::o;7253:742::-;7368:12;7397:7;7393:595;;;-1:-1:-1;7428:10:61;7421:17;;7393:595;7542:17;;:21;7538:439;;7805:10;7799:17;7866:15;7853:10;7849:2;7845:19;7838:44;7753:148;7941:20;;-1:-1:-1;;;7941:20:61;;;;;;;;;;;;;;;;;7948:12;;7941:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;
Swarm Source
ipfs://127f467dd7997c47df98a559f890d614307e268cbd07feca4f7c83135a70dc46
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.