From 0dff045bd26885af4e2af29a8a292d2b08fcea49 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Wed, 22 May 2024 09:21:30 +0700 Subject: [PATCH 01/15] init: refactor feeSharingCollector --- contracts/events/ProtocolSettingsEvents.sol | 2 +- .../FeeSharingCollector.sol | 428 ++--- .../FeeSharingCollectorStorage.sol | 12 +- contracts/interfaces/IWrappedNativeToken.sol | 12 + .../interfaces/IWrappedNativeTokenERC20.sol | 11 + .../mockup/FeeSharingCollectorMockup.sol | 4 +- .../testhelpers/TestWrappedNativeToken.sol | 775 +++++++++ contracts/testhelpers/WrappedNativeToken.sol | 754 ++++++++ .../LoanToken_iNativeToken.json | 1517 ++++++++++++++++ .../LoanToken_iNativeToken.json | 1517 ++++++++++++++++ .../rskMainnet/WrappedNativeToken.json | 276 +++ .../rskTestnet/WrappedNativeToken.json | 276 +++ hardhat/tasks/feeSharingCollector.js | 58 +- tests/FeeSharingCollectorTest.js | 1543 ++++++++++------- tests/swaps/SwapsExternal.js | 2 +- 15 files changed, 6291 insertions(+), 896 deletions(-) create mode 100644 contracts/interfaces/IWrappedNativeToken.sol create mode 100644 contracts/interfaces/IWrappedNativeTokenERC20.sol create mode 100644 contracts/testhelpers/TestWrappedNativeToken.sol create mode 100644 contracts/testhelpers/WrappedNativeToken.sol create mode 100644 deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json create mode 100644 deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json create mode 100644 external/deployments/rskMainnet/WrappedNativeToken.json create mode 100644 external/deployments/rskTestnet/WrappedNativeToken.json diff --git a/contracts/events/ProtocolSettingsEvents.sol b/contracts/events/ProtocolSettingsEvents.sol index 216548667..9b59133a6 100644 --- a/contracts/events/ProtocolSettingsEvents.sol +++ b/contracts/events/ProtocolSettingsEvents.sol @@ -83,7 +83,7 @@ contract ProtocolSettingsEvents is ModulesCommonEvents { uint256 lendingAmount, uint256 tradingAmount, uint256 borrowingAmount, - uint256 wRBTCConverted + uint256 wrappedNativeTokenConverted ); event WithdrawLendingFees( diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index 72639273a..bbd9438e9 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -42,8 +42,8 @@ import "../../interfaces/IConverterAMM.sol"; * * The protocol initially collects fees in all tokens. * Then the FeeSharingCollector wihtdraws fees from the protocol. - * When the fees are withdrawn all the tokens except SOV will be converted to wRBTC - * and then transferred to wRBTC loan pool. + * When the fees are withdrawn all the tokens except SOV will be converted to wrappedNativeToken + * and then transferred to wrappedNativeToken loan pool. * For SOV, it will be directly deposited into the feeSharingCollector from the protocol. * */ contract FeeSharingCollector is @@ -56,14 +56,14 @@ contract FeeSharingCollector is using SafeERC20 for IERC20; address constant ZERO_ADDRESS = address(0); - address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT = - address(uint160(uint256(keccak256("RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT")))); + address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = + address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); /* Events */ - /// @notice Deprecated event after the unification between wrbtc & rbtc + /// @notice Deprecated event after the unification between wrappedNativeToken & nativeToken // event FeeWithdrawn(address indexed sender, address indexed token, uint256 amount); - event FeeWithdrawnInRBTC(address indexed sender, uint256 amount); + event FeeWithdrawnInNativeToken(address indexed sender, uint256 amount); /// @notice An event emitted when tokens transferred. event TokensTransferred(address indexed sender, address indexed token, uint256 amount); @@ -92,7 +92,7 @@ contract FeeSharingCollector is * * @param sender sender who initiate the withdrawn amm fees. * @param converter the converter address. - * @param amount total amount of fee (Already converted to WRBTC). + * @param amount total amount of fee (Already converted to wrappedNativeToken). */ event FeeAMMWithdrawn(address indexed sender, address indexed converter, uint256 amount); @@ -102,18 +102,18 @@ contract FeeSharingCollector is /// @notice An event emitted when converter address has been removed from whitelist. event UnwhitelistedConverter(address indexed sender, address converter); - event RBTCWithdrawn(address indexed sender, address indexed receiver, uint256 amount); + event NativeTokenWithdrawn(address indexed sender, address indexed receiver, uint256 amount); - event SetWrbtcToken( + event SetWrappedNativeToken( address indexed sender, - address indexed oldWrbtcToken, - address indexed newWrbtcToken + address indexed oldWrappedNativeToken, + address indexed newWrappedNativeToken ); - event SetLoanTokenWrbtc( + event SetLoanWrappedNativeToken( address indexed sender, - address indexed oldLoanTokenWrbtc, - address indexed newLoanTokenWrbtc + address indexed oldLoanWrappedNativeToken, + address indexed newLoanWrappedNativeToken ); event SetProtocolAddress(address indexed sender, address _protocolAddress); @@ -130,37 +130,44 @@ contract FeeSharingCollector is /* Functions */ - /// @dev fallback function to support rbtc transfer when unwrap the wrbtc. + /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken. function() external payable {} /** * @dev initialize function for fee sharing collector proxy - * @param wrbtcToken wrbtc token address - * @param loanWrbtcToken address of loan token wrbtc (IWrbtc) + * @param wrappedNativeToken wrappedNativeToken token address + * @param loanWrappedNativeToken address of loan token wrappedNativeToken (IWrappedNativeToken) */ function initialize( - address wrbtcToken, - address loanWrbtcToken + address wrappedNativeToken, + address loanWrappedNativeToken ) external onlyOwner oneTimeExecution(this.initialize.selector) { require( - wrbtcTokenAddress == address(0) && loanTokenWrbtcAddress == address(0), - "wrbtcToken or loanWrbtcToken has been initialized" + wrappedNativeTokenAddress == address(0) && loanWrappedNativeTokenAddress == address(0), + "wrappedNativeToken or loanWrappedNativeToken has been initialized" ); - setWrbtcToken(wrbtcToken); - setLoanTokenWrbtc(loanWrbtcToken); + setWrappedNativeToken(wrappedNativeToken); + setLoanWrappedNativeToken(loanWrappedNativeToken); } /** - * @notice Set the wrbtc token address of fee sharing collector. + * @notice Set the wrappedNativeToken token address of fee sharing collector. * * only owner can perform this action. * - * @param newWrbtcTokenAddress The new address of the wrbtc token. + * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken token. * */ - function setWrbtcToken(address newWrbtcTokenAddress) public onlyOwner { - require(Address.isContract(newWrbtcTokenAddress), "newWrbtcTokenAddress not a contract"); - emit SetWrbtcToken(msg.sender, wrbtcTokenAddress, newWrbtcTokenAddress); - wrbtcTokenAddress = newWrbtcTokenAddress; + function setWrappedNativeToken(address newWrappedNativeTokenAddress) public onlyOwner { + require( + Address.isContract(newWrappedNativeTokenAddress), + "newWrappedNativeTokenAddress not a contract" + ); + emit SetWrappedNativeToken( + msg.sender, + wrappedNativeTokenAddress, + newWrappedNativeTokenAddress + ); + wrappedNativeTokenAddress = newWrappedNativeTokenAddress; } /** @@ -180,25 +187,29 @@ contract FeeSharingCollector is } /** - * @notice Set the loan wrbtc token address of fee sharing collector. + * @notice Set the loan wrappedNativeToken token address of fee sharing collector. * * only owner can perform this action. * - * @param newLoanTokenWrbtcAddress The new address of the loan wrbtc token. + * @param newLoanWrappedNativeTokenAddress The new address of the loan wrappedNativeToken token. * */ - function setLoanTokenWrbtc(address newLoanTokenWrbtcAddress) public onlyOwner { + function setLoanWrappedNativeToken(address newLoanWrappedNativeTokenAddress) public onlyOwner { require( - Address.isContract(newLoanTokenWrbtcAddress), - "newLoanTokenWrbtcAddress not a contract" + Address.isContract(newLoanWrappedNativeTokenAddress), + "newLoanWrappedNativeTokenAddress not a contract" + ); + emit SetLoanWrappedNativeToken( + msg.sender, + loanWrappedNativeTokenAddress, + newLoanWrappedNativeTokenAddress ); - emit SetLoanTokenWrbtc(msg.sender, loanTokenWrbtcAddress, newLoanTokenWrbtcAddress); - loanTokenWrbtcAddress = newLoanTokenWrbtcAddress; + loanWrappedNativeTokenAddress = newLoanWrappedNativeTokenAddress; } /** * @notice Withdraw fees for the given token: * lendingFee + tradingFee + borrowingFee - * the fees (except SOV) will be converted in wRBTC form, and then will be transferred to wRBTC loan pool. + * the fees (except SOV) will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool. * For SOV, it will be directly deposited into the feeSharingCollector from the protocol. * * @param _tokens array address of the token @@ -211,71 +222,79 @@ contract FeeSharingCollector is ); } - uint256 wrbtcAmountWithdrawn = protocol.withdrawFees(_tokens, address(this)); + uint256 wrappedNativeTokenAmountWithdrawn = protocol.withdrawFees(_tokens, address(this)); - IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress); + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); - if (wrbtcAmountWithdrawn > 0) { - // unwrap the wrbtc to rbtc, and hold the rbtc. - wrbtcToken.withdraw(wrbtcAmountWithdrawn); + if (wrappedNativeTokenAmountWithdrawn > 0) { + // unwrap the wrappedNativeToken to nativeToken, and hold the nativeToken. + wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn); /// @notice Update unprocessed amount of tokens uint96 amount96 = safe96( - wrbtcAmountWithdrawn, - "FeeSharingCollector::withdrawFees: wrbtc token amount exceeds 96 bits" + wrappedNativeTokenAmountWithdrawn, + "FeeSharingCollector::withdrawFees: wrappedNativeToken token amount exceeds 96 bits" ); - _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96); + _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96); } - // note deprecated event since we unify the wrbtc & rbtc - // emit FeeWithdrawn(msg.sender, RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount); + // note deprecated event since we unify the wrappedNativeToken & nativeToken + // emit FeeWithdrawn(msg.sender, NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount); // note new emitted event - emit FeeWithdrawnInRBTC(msg.sender, wrbtcAmountWithdrawn); + emit FeeWithdrawnInNativeToken(msg.sender, wrappedNativeTokenAmountWithdrawn); } /** * @notice Withdraw amm fees for the given converter addresses: * protocolFee from the conversion - * the fees will be converted in wRBTC form, and then will be transferred to wRBTC loan pool + * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool * * @param _converters array addresses of the converters * */ function withdrawFeesAMM(address[] memory _converters) public { - IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress); + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); // Validate _validateWhitelistedConverter(_converters); uint96 totalPoolTokenAmount; for (uint256 i = 0; i < _converters.length; i++) { - uint256 wrbtcAmountWithdrawn = IConverterAMM(_converters[i]).withdrawFees( + uint256 wrappedNativeTokenAmountWithdrawn = IConverterAMM(_converters[i]).withdrawFees( address(this) ); - if (wrbtcAmountWithdrawn > 0) { - // unwrap wrbtc to rbtc, and hold the rbtc - wrbtcToken.withdraw(wrbtcAmountWithdrawn); + if (wrappedNativeTokenAmountWithdrawn > 0) { + // unwrap wrappedNativeToken to nativeToken, and hold the nativeToken + wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn); /// @notice Update unprocessed amount of tokens uint96 amount96 = safe96( - wrbtcAmountWithdrawn, - "FeeSharingCollector::withdrawFeesAMM: wrbtc token amount exceeds 96 bits" + wrappedNativeTokenAmountWithdrawn, + "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken token amount exceeds 96 bits" ); totalPoolTokenAmount = add96( totalPoolTokenAmount, amount96, - "FeeSharingCollector::withdrawFeesAMM: total wrbtc token amount exceeds 96 bits" + "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken token amount exceeds 96 bits" ); - emit FeeAMMWithdrawn(msg.sender, _converters[i], wrbtcAmountWithdrawn); + emit FeeAMMWithdrawn( + msg.sender, + _converters[i], + wrappedNativeTokenAmountWithdrawn + ); } } if (totalPoolTokenAmount > 0) { - _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); + _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); } } @@ -294,11 +313,13 @@ contract FeeSharingCollector is bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount); require(success, "Staking::transferTokens: token transfer failed"); - // if _token is wrbtc, need to unwrap it to rbtc - IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress); - if (_token == address(wrbtcToken)) { - wrbtcToken.withdraw(_amount); - _token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT; + // if _token is wrappedNativeToken, need to unwrap it to nativeToken + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); + if (_token == address(wrappedNativeToken)) { + wrappedNativeToken.withdraw(_amount); + _token = NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; } _addCheckpoint(_token, _amount); @@ -307,15 +328,15 @@ contract FeeSharingCollector is } /** - * @notice Transfer RBTC / native tokens to this contract. - * @dev We just write checkpoint here (based on the rbtc value that is sent) in a separate methods + * @notice Transfer NativeToken / native tokens to this contract. + * @dev We just write checkpoint here (based on the nativeToken value that is sent) in a separate methods * in order to prevent adding checkpoints too often. * */ - function transferRBTC() external payable { + function transferNativeToken() external payable { uint96 _amount = uint96(msg.value); - require(_amount > 0, "FeeSharingCollector::transferRBTC: invalid value"); + require(_amount > 0, "FeeSharingCollector::transferNativeToken: invalid value"); - _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount); + _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount); emit TokensTransferred(msg.sender, ZERO_ADDRESS, _amount); } @@ -381,11 +402,11 @@ contract FeeSharingCollector is } processedCheckpoints[user][_token] = end; - if (loanTokenWrbtcAddress == _token) { - // We will change, so that feeSharingCollector will directly burn then loanToken (IWRBTC) to rbtc and send to the user --- by call burnToBTC function - ILoanTokenWRBTC(_token).burnToBTC(_receiver, amount, false); + if (loanWrappedNativeTokenAddress == _token) { + // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function + ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false); } else { - // Previously it directly send the loanToken to the user + // Previously it directly send the loanWrappedNativeToken to the user require( IERC20(_token).transfer(_receiver, amount), "FeeSharingCollector::withdraw: withdrawal failed" @@ -408,7 +429,7 @@ contract FeeSharingCollector is * * This function will directly burnToBTC and use the msg.sender (user) as the receiver * - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _maxCheckpoints Maximum number of checkpoints to be processed. Must be positive value. * @param _receiver The receiver of tokens or msg.sender * */ @@ -466,15 +487,15 @@ contract FeeSharingCollector is } } - function validRBTCBasedTokens(address[] memory _tokens) private view { + function validNativeTokenBasedTokens(address[] memory _tokens) private view { for (uint256 i = 0; i < _tokens.length; i++) { address _token = _tokens[i]; if ( - _token != RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT && - _token != wrbtcTokenAddress && - _token != loanTokenWrbtcAddress + _token != NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT && + _token != wrappedNativeTokenAddress && + _token != loanWrappedNativeTokenAddress ) { - revert("only rbtc-based tokens are allowed"); + revert("only nativeToken-based tokens are allowed"); } } } @@ -509,7 +530,7 @@ contract FeeSharingCollector is _receiver = msg.sender; } - uint256 rbtcAmountToSend; + uint256 nativeTokenAmountToSend; for (uint256 i = 0; i < _tokens.length; i++) { TokenWithSkippedCheckpointsWithdraw memory tokenData = _tokens[i]; @@ -526,17 +547,17 @@ contract FeeSharingCollector is : previousProcessedUserCheckpoints; if ( - tokenData.tokenAddress == wrbtcTokenAddress || - tokenData.tokenAddress == loanTokenWrbtcAddress || - tokenData.tokenAddress == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + tokenData.tokenAddress == wrappedNativeTokenAddress || + tokenData.tokenAddress == loanWrappedNativeTokenAddress || + tokenData.tokenAddress == NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ) { - (totalAmount, endToken) = _withdrawRbtcTokenStartingFromCheckpoint( + (totalAmount, endToken) = _withdrawNativeTokenStartingFromCheckpoint( tokenData.tokenAddress, tokenData.fromCheckpoint, _maxCheckpoints, _receiver ); - rbtcAmountToSend = rbtcAmountToSend.add(totalAmount); + nativeTokenAmountToSend = nativeTokenAmountToSend.add(totalAmount); } else { (, endToken) = _withdrawStartingFromCheckpoint( tokenData.tokenAddress, @@ -554,38 +575,38 @@ contract FeeSharingCollector is ); } - if (rbtcAmountToSend > 0) { - // send all rbtc withdrawal - (bool success, ) = _receiver.call.value(rbtcAmountToSend)(""); - require(success, "FeeSharingCollector::withdrawRBTC: Withdrawal failed"); + if (nativeTokenAmountToSend > 0) { + // send all nativeToken withdrawal + (bool success, ) = _receiver.call.value(nativeTokenAmountToSend)(""); + require(success, "FeeSharingCollector::withdra: Withdrawal failed"); - emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend); + emit NativeTokenWithdrawn(msg.sender, _receiver, nativeTokenAmountToSend); } } /** * @dev Function to wrap: - * 1. regular withdrawal for both rbtc & non-rbtc token - * 2. skipped checkpoints withdrawal for both rbtc & non-rbtc token + * 1. regular withdrawal for both nativeToken & non-nativeToken token + * 2. skipped checkpoints withdrawal for both nativeToken & non-nativeToken token * - * @param _nonRbtcTokensRegularWithdraw array of non-rbtc token address with no skipped checkpoints that will be withdrawn - * @param _rbtcTokensRegularWithdraw array of rbtc token address with no skipped checkpoints that will be withdrawn - * @param _tokensWithSkippedCheckpoints array of rbtc & non-rbtc TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn + * @param _nonNativeTokensRegularWithdraw array of non-nativeToken token address with no skipped checkpoints that will be withdrawn + * @param _nativeTokensRegularWithdraw array of nativeToken token address with no skipped checkpoints that will be withdrawn + * @param _tokensWithSkippedCheckpoints array of nativeToken & non-nativeToken TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn * */ function claimAllCollectedFees( - address[] calldata _nonRbtcTokensRegularWithdraw, - address[] calldata _rbtcTokensRegularWithdraw, + address[] calldata _nonNativeTokensRegularWithdraw, + address[] calldata _nativeTokensRegularWithdraw, TokenWithSkippedCheckpointsWithdraw[] calldata _tokensWithSkippedCheckpoints, uint32 _maxCheckpoints, address _receiver ) external nonReentrant { uint256 totalProcessedCheckpoints; - /** Process normal multiple withdrawal for RBTC based tokens */ - if (_rbtcTokensRegularWithdraw.length > 0) { - totalProcessedCheckpoints = _withdrawRbtcTokens( - _rbtcTokensRegularWithdraw, + /** Process normal multiple withdrawal for NativeToken based tokens */ + if (_nativeTokensRegularWithdraw.length > 0) { + totalProcessedCheckpoints = _withdrawNativeTokens( + _nativeTokensRegularWithdraw, _maxCheckpoints, _receiver ); @@ -595,17 +616,17 @@ contract FeeSharingCollector is ); } - /** Process normal non-rbtc token withdrawal */ - for (uint256 i = 0; i < _nonRbtcTokensRegularWithdraw.length; i++) { + /** Process normal non-nativeToken token withdrawal */ + for (uint256 i = 0; i < _nonNativeTokensRegularWithdraw.length; i++) { if (_maxCheckpoints == 0) break; uint256 endTokenCheckpoint; - address _nonRbtcTokenAddress = _nonRbtcTokensRegularWithdraw[i]; + address _nonNativeTokenAddress = _nonNativeTokensRegularWithdraw[i]; /** starting checkpoint is the previous processedCheckpoints for token */ - uint256 startingCheckpoint = processedCheckpoints[msg.sender][_nonRbtcTokenAddress]; + uint256 startingCheckpoint = processedCheckpoints[msg.sender][_nonNativeTokenAddress]; - (, endTokenCheckpoint) = _withdraw(_nonRbtcTokenAddress, _maxCheckpoints, _receiver); + (, endTokenCheckpoint) = _withdraw(_nonNativeTokenAddress, _maxCheckpoints, _receiver); uint256 _previousUsedCheckpoint = endTokenCheckpoint.sub(startingCheckpoint); if (startingCheckpoint > 0) { @@ -647,25 +668,27 @@ contract FeeSharingCollector is (totalAmount, endTokenCheckpoint) = _withdraw(_token, _maxCheckpoints, _receiver); } - function _withdrawRbtcToken( + function _withdrawNativeToken( address _token, uint32 _maxCheckpoints ) internal returns (uint256 totalAmount, uint256 endTokenCheckpoint) { address user = msg.sender; - IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress); + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); - (totalAmount, endTokenCheckpoint) = _getRBTCBalance(_token, user, _maxCheckpoints); + (totalAmount, endTokenCheckpoint) = _getNativeTokenBalance(_token, user, _maxCheckpoints); if (totalAmount > 0) { processedCheckpoints[user][_token] = endTokenCheckpoint; - if (_token == address(wrbtcToken)) { - // unwrap the wrbtc - wrbtcToken.withdraw(totalAmount); - } else if (_token == loanTokenWrbtcAddress) { - // pull out the iWRBTC to rbtc to this feeSharingCollector contract - /** @dev will use the burned result from IWRBTC to RBTC as return total amount */ - totalAmount = ILoanTokenWRBTC(loanTokenWrbtcAddress).burnToBTC( + if (_token == address(wrappedNativeToken)) { + // unwrap the wrappedNativeToken + wrappedNativeToken.withdraw(totalAmount); + } else if (_token == loanWrappedNativeTokenAddress) { + // pull out the iWrappedNativeToken to nativeToken to this feeSharingCollector contract + /** @dev will use the burned result from IWrappedNativeToken to NativeToken as return total amount */ + totalAmount = ILoanWrappedNativeToken(loanWrappedNativeTokenAddress).burnToBTC( address(this), totalAmount, false @@ -675,41 +698,41 @@ contract FeeSharingCollector is } /** - * @dev withdraw all of the RBTC balance based on particular checkpoints + * @dev withdraw all of the NativeToken balance based on particular checkpoints * - * This function will withdraw RBTC balance which is passed as _token param, so it could be either of these: - * - rbtc balance or - * - wrbtc balance which will be unwrapped to rbtc or - * - iwrbtc balance which will be unwrapped to rbtc or + * This function will withdraw NativeToken balance which is passed as _token param, so it could be either of these: + * - nativeToken balance or + * - wrappedNativeToken balance which will be unwrapped to nativeToken or + * - iWrappedNativeToken balance which will be unwrapped to nativeToken or * * - * @param _tokens array of either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address + * @param _tokens array of either NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address * @param _maxCheckpoints Maximum number of checkpoints to be processed to workaround block gas limit * @param _receiver An optional tokens receiver (msg.sender used if 0) */ - function _withdrawRbtcTokens( + function _withdrawNativeTokens( address[] memory _tokens, uint32 _maxCheckpoints, address _receiver ) internal returns (uint256 totalProcessedCheckpoints) { - validRBTCBasedTokens(_tokens); + validNativeTokenBasedTokens(_tokens); if (_receiver == ZERO_ADDRESS) { _receiver = msg.sender; } - uint256 rbtcAmountToSend; + uint256 nativeTokenAmountToSend; for (uint256 i = 0; i < _tokens.length; i++) { if (_maxCheckpoints == 0) break; address _token = _tokens[i]; uint256 startingCheckpoint = processedCheckpoints[msg.sender][_token]; - (uint256 totalAmount, uint256 endToken) = _withdrawRbtcToken( + (uint256 totalAmount, uint256 endToken) = _withdrawNativeToken( _tokens[i], _maxCheckpoints ); - rbtcAmountToSend = rbtcAmountToSend.add(totalAmount); + nativeTokenAmountToSend = nativeTokenAmountToSend.add(totalAmount); uint256 _previousUsedCheckpoint = endToken.sub(startingCheckpoint); if (startingCheckpoint > 0) { @@ -723,20 +746,20 @@ contract FeeSharingCollector is ); } - // send all rbtc - if (rbtcAmountToSend > 0) { - (bool success, ) = _receiver.call.value(rbtcAmountToSend)(""); - require(success, "FeeSharingCollector::withdrawRBTC: Withdrawal failed"); + // send all nativeToken + if (nativeTokenAmountToSend > 0) { + (bool success, ) = _receiver.call.value(nativeTokenAmountToSend)(""); + require(success, "FeeSharingCollector::withdrawNativeToken: Withdrawal failed"); - emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend); + emit NativeTokenWithdrawn(msg.sender, _receiver, nativeTokenAmountToSend); } } /** - * @dev Withdraw either specific RBTC related token balance or all RBTC related tokens balances. - * RBTC related here means, it could be either rbtc, wrbtc, or iwrbtc, depends on the _token param. + * @dev Withdraw either specific NativeToken related token balance or all NativeToken related tokens balances. + * NativeToken related here means, it could be either nativeToken, wrappedNativeToken, or iWrappedNativeToken, depends on the _token param. */ - function _withdrawRbtcTokenStartingFromCheckpoint( + function _withdrawNativeTokenStartingFromCheckpoint( address _token, uint256 _fromCheckpoint, uint32 _maxCheckpoints, @@ -749,14 +772,14 @@ contract FeeSharingCollector is if (prevFromCheckpoint > processedCheckpoints[msg.sender][_token]) { processedCheckpoints[msg.sender][_token] = prevFromCheckpoint; } - return _withdrawRbtcToken(_token, _maxCheckpoints); + return _withdrawNativeToken(_token, _maxCheckpoints); } /** * @dev Returns first user's checkpoint with weighted stake > 0 * * @param _user The address of the user or contract. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints. * @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error * @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees] @@ -774,7 +797,7 @@ contract FeeSharingCollector is * @dev Returns first user's checkpoint with weighted stake > 0 * * @param _user The address of the user or contract. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints. * @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error * @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees] @@ -826,7 +849,7 @@ contract FeeSharingCollector is /** * @notice Get the accumulated loan pool fee of the message sender. * @param _user The address of the user or contract. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @return The accumulated fee for the message sender. * */ function getAccumulatedFees(address _user, address _token) public view returns (uint256) { @@ -846,7 +869,7 @@ contract FeeSharingCollector is * @dev This function is required to keep consistent with caching of weighted voting power when claiming fees * * @param _user The address of a user (staker) or contract. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _startFrom Checkpoint to start calculating fees from. * @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user * @return The accumulated fees rewards for the _user in the given checkpoints interval: [_startFrom, _startFrom + maxCheckpoints]. @@ -870,7 +893,7 @@ contract FeeSharingCollector is * if there is no more fees, it will return empty array. * * @param _user The address of a user (staker) or contract. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _startFrom Checkpoint to start calculating fees from. * @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user * @return The next checkpoint num which is the starting point to fetch all of the fees, array of calculated fees. @@ -909,7 +932,7 @@ contract FeeSharingCollector is * @notice Gets accumulated fees for a user starting from a given checkpoint * * @param _user Address of the user's account. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token. * @param _maxCheckpoints Max checkpoints to process at once to fit into block gas limit * @param _startFrom Checkpoint num to start calculations from * @@ -972,7 +995,7 @@ contract FeeSharingCollector is * they are not considered by the withdrawing logic (to avoid inconsistencies). * * @param _start Start of the range. - * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of a pool token. + * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of a pool token. * @param _maxCheckpoints Checkpoint index incremental. * */ function _getEndOfRange( @@ -1104,20 +1127,23 @@ contract FeeSharingCollector is } } - function withdrawWRBTC(address receiver, uint256 wrbtcAmount) external onlyOwner { - IERC20 wrbtcToken = IERC20(wrbtcTokenAddress); + function withdrawWrappedNativeToken( + address receiver, + uint256 wrappedNativeTokenAmount + ) external onlyOwner { + IERC20 wrappedNativeToken = IERC20(wrappedNativeTokenAddress); - uint256 balance = wrbtcToken.balanceOf(address(this)); - require(wrbtcAmount <= balance, "Insufficient balance"); + uint256 balance = wrappedNativeToken.balanceOf(address(this)); + require(wrappedNativeTokenAmount <= balance, "Insufficient balance"); - wrbtcToken.safeTransfer(receiver, wrbtcAmount); + wrappedNativeToken.safeTransfer(receiver, wrappedNativeTokenAmount); } /** * @dev This function is dedicated to recover the wrong fee allocation for the 4 year vesting contracts. * This function can only be called once * The affected tokens to be withdrawn - * 1. RBTC + * 1. NativeToken * 2. ZUSD * 3. SOV * The amount for all of the tokens above is hardcoded @@ -1128,18 +1154,18 @@ contract FeeSharingCollector is oneTimeExecution(this.recoverIncorrectAllocatedFees.selector) onlyOwner { - uint256 rbtcAmount = 878778886164898400; + uint256 nativeTokenAmount = 878778886164898400; uint256 zusdAmount = 16658600400155126000000; uint256 sovAmount = 6275898259771202000000; address zusdToken = 0xdB107FA69E33f05180a4C2cE9c2E7CB481645C2d; address sovToken = 0xEFc78fc7d48b64958315949279Ba181c2114ABBd; - // Withdraw rbtc - (bool success, ) = owner().call.value(rbtcAmount)(""); + // Withdraw nativeToken + (bool success, ) = owner().call.value(nativeTokenAmount)(""); require( success, - "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal rbtc failed" + "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal nativeToken failed" ); // Withdraw ZUSD @@ -1150,96 +1176,99 @@ contract FeeSharingCollector is } /** - * @dev view function that calculate the total RBTC that includes: - * - RBTC - * - WRBTC - * - iWRBTC * iWRBTC.tokenPrice() + * @dev view function that calculate the total NativeToken that includes: + * - NativeToken + * - WrappedNativeToken + * - iWrappedNativeToken * iWrappedNativeToken.tokenPrice() * @param _user address of the user. - * @return rbtc balance of the given user's address. + * @return nativeToken balance of the given user's address. */ - function getAccumulatedRBTCFeeBalances(address _user) external view returns (uint256) { + function getAccumulatedNativeTokenFeeBalances(address _user) external view returns (uint256) { ( - uint256 _rbtcAmount, - uint256 _wrbtcAmount, - uint256 _iWrbtcAmount, + uint256 _nativeTokenAmount, + uint256 _wrappedNativeTokenAmount, + uint256 _iWrappedNativeTokenAmount, , , - ) = _getRBTCBalances(_user, 0); - uint256 iWRBTCAmountInRBTC = _iWrbtcAmount - .mul(ILoanTokenWRBTC(loanTokenWrbtcAddress).tokenPrice()) + ) = _getNativeTokenBalances(_user, 0); + uint256 iWrappedNativeTokenAmountInNativeToken = _iWrappedNativeTokenAmount + .mul(ILoanWrappedNativeToken(loanWrappedNativeTokenAddress).tokenPrice()) .div(1e18); - return _rbtcAmount.add(_wrbtcAmount).add(iWRBTCAmountInRBTC); + return + _nativeTokenAmount.add(_wrappedNativeTokenAmount).add( + iWrappedNativeTokenAmountInNativeToken + ); } /** - * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc) + * @dev private function that responsible to calculate the user's token that has NativeToken as underlying token (nativeToken, wrappedNativeToken, iWrappedNativeToken) * * @param _user address of the user. * @param _maxCheckpoints maximum checkpoints. * - * @return _rbtcAmount rbtc amount - * @return _wrbtcAmount wrbtc amount - * @return _iWrbtcAmount iWrbtc (wrbtc lending pool token) amount * token price - * @return _endRBTC end time of accumulated fee calculation for rbtc - * @return _endWRBTC end time of accumulated fee calculation for wrbtc - * @return _endIWRBTC end time of accumulated fee calculation for iwrbtc + * @return _nativeTokenAmount nativeToken amount + * @return _wrappedNativeTokenAmount wrappedNativeToken amount + * @return _iWrappedNativeTokenAmount iWrappedNativeToken (wrappedNativeToken lending pool token) amount * token price + * @return _endNativeToken end time of accumulated fee calculation for nativeToken + * @return _endWrappedNativeToken end time of accumulated fee calculation for wrappedNativeToken + * @return _endIWrappedNativeToken end time of accumulated fee calculation for iWrappedNativeToken */ - function _getRBTCBalances( + function _getNativeTokenBalances( address _user, uint32 _maxCheckpoints ) private view returns ( - uint256 _rbtcAmount, - uint256 _wrbtcAmount, - uint256 _iWrbtcAmount, - uint256 _endRBTC, - uint256 _endWRBTC, - uint256 _endIWRBTC + uint256 _nativeTokenAmount, + uint256 _wrappedNativeTokenAmount, + uint256 _iWrappedNativeTokenAmount, + uint256 _endNativeToken, + uint256 _endWrappedNativeToken, + uint256 _endIWrappedNativeToken ) { - (_rbtcAmount, _endRBTC) = _getAccumulatedFees({ + (_nativeTokenAmount, _endNativeToken) = _getAccumulatedFees({ _user: _user, - _token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + _token: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, _startFrom: 0, _maxCheckpoints: _maxCheckpoints }); - (_wrbtcAmount, _endWRBTC) = _getAccumulatedFees({ + (_wrappedNativeTokenAmount, _endWrappedNativeToken) = _getAccumulatedFees({ _user: _user, - _token: wrbtcTokenAddress, + _token: wrappedNativeTokenAddress, _startFrom: 0, _maxCheckpoints: _maxCheckpoints }); - (_iWrbtcAmount, _endIWRBTC) = _getAccumulatedFees({ + (_iWrappedNativeTokenAmount, _endIWrappedNativeToken) = _getAccumulatedFees({ _user: _user, - _token: loanTokenWrbtcAddress, + _token: loanWrappedNativeTokenAddress, _startFrom: 0, _maxCheckpoints: _maxCheckpoints }); } /** - * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc) + * @dev private function that responsible to calculate the user's token that has NativeToken as underlying token (nativeToken, wrappedNativeToken, iWrappedNativeToken) * - * @param _token either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address + * @param _token either NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address * @param _user address of the user. * @param _maxCheckpoints maximum checkpoints. * - * @return _tokenAmount token (rbtc, or wrbtc, or iwrbtc) amount - * @return _endToken end time of accumulated fee calculation for token (rbtc, or wrbtc, or iwrbtc ) + * @return _tokenAmount token (nativeToken, or wrappedNativeToken, or iWrappedNativeToken) amount + * @return _endToken end time of accumulated fee calculation for token (nativeToken, or wrappedNativeToken, or iWrappedNativeToken ) */ - function _getRBTCBalance( + function _getNativeTokenBalance( address _token, address _user, uint32 _maxCheckpoints ) internal view returns (uint256 _tokenAmount, uint256 _endToken) { if ( - _token == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT || - _token == wrbtcTokenAddress || - _token == loanTokenWrbtcAddress + _token == NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT || + _token == wrappedNativeTokenAddress || + _token == loanWrappedNativeTokenAddress ) { (_tokenAmount, _endToken) = _getAccumulatedFees({ _user: _user, @@ -1248,7 +1277,9 @@ contract FeeSharingCollector is _maxCheckpoints: _maxCheckpoints }); } else { - revert("FeeSharingCollector::_getRBTCBalance: only rbtc-based tokens are allowed"); + revert( + "FeeSharingCollector::_getNativeTokenBalance: only nativeToken-based tokens are allowed" + ); } } @@ -1266,12 +1297,7 @@ contract FeeSharingCollector is } } -/* Interfaces */ -interface ILoanToken { - function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount); -} - -interface ILoanTokenWRBTC { +interface ILoanWrappedNativeToken { function burnToBTC( address receiver, uint256 burnAmount, diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol index a6881517c..790f8c810 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol @@ -6,7 +6,7 @@ import "../../interfaces/IERC20.sol"; import "../IFeeSharingCollector.sol"; import "../Staking/interfaces/IStaking.sol"; import "../../mixins/EnumerableAddressSet.sol"; -import "../../interfaces/IWrbtcERC20.sol"; +import "../../interfaces/IWrappedNativeTokenERC20.sol"; /** * @title FeeSharingCollectorStorage contact @@ -79,12 +79,12 @@ contract FeeSharingCollectorStorage is Ownable { /** * @dev Wrapped native token address */ - address public wrbtcTokenAddress; + address public wrappedNativeTokenAddress; /** * @dev Wrapped native token loan token address */ - address public loanTokenWrbtcAddress; + address public loanWrappedNativeTokenAddress; /** * @dev Prevents a contract from calling itself, directly or indirectly. @@ -110,16 +110,16 @@ interface IProtocol { * @param tokens The array address of the token instance. * @param receiver The address of the withdrawal recipient. * - * @return The withdrawn total amount in wRBTC + * @return The withdrawn total amount in wrappedNativeToken * */ function withdrawFees( address[] calldata tokens, address receiver - ) external returns (uint256 totalWRBTCWithdrawn); + ) external returns (uint256 totalWrappedNativeTokenWithdrawn); function underlyingToLoanPool(address token) external view returns (address); - function wrbtcToken() external view returns (IWrbtcERC20); + function wrappedNativeToken() external view returns (IWrappedNativeTokenERC20); function getSovTokenAddress() external view returns (address); } diff --git a/contracts/interfaces/IWrappedNativeToken.sol b/contracts/interfaces/IWrappedNativeToken.sol new file mode 100644 index 000000000..57b89fde6 --- /dev/null +++ b/contracts/interfaces/IWrappedNativeToken.sol @@ -0,0 +1,12 @@ +/** + * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity >=0.5.0 <0.6.0; + +interface IWrappedNativeToken { + function deposit() external payable; + + function withdraw(uint256 wad) external; +} diff --git a/contracts/interfaces/IWrappedNativeTokenERC20.sol b/contracts/interfaces/IWrappedNativeTokenERC20.sol new file mode 100644 index 000000000..aca2a529c --- /dev/null +++ b/contracts/interfaces/IWrappedNativeTokenERC20.sol @@ -0,0 +1,11 @@ +/** + * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity >=0.5.0 <0.6.0; + +import "./IWrappedNativeToken.sol"; +import "./IERC20.sol"; + +contract IWrappedNativeTokenERC20 is IWrappedNativeToken, IERC20 {} diff --git a/contracts/mockup/FeeSharingCollectorMockup.sol b/contracts/mockup/FeeSharingCollectorMockup.sol index 67b092663..1a39acc06 100644 --- a/contracts/mockup/FeeSharingCollectorMockup.sol +++ b/contracts/mockup/FeeSharingCollectorMockup.sol @@ -53,12 +53,12 @@ contract FeeSharingCollectorMockup is FeeSharingCollector { return _getEndOfRange(0, _token, 0); } - function getRBTCBalance( + function getNativeTokenBalance( address _token, address _user, uint32 _maxCheckpoints ) public view returns (uint256 _tokenAmount, uint256 _endToken) { - return _getRBTCBalance(_token, _user, _maxCheckpoints); + return _getNativeTokenBalance(_token, _user, _maxCheckpoints); } function testWithdrawReentrancy( diff --git a/contracts/testhelpers/TestWrappedNativeToken.sol b/contracts/testhelpers/TestWrappedNativeToken.sol new file mode 100644 index 000000000..acbb5e729 --- /dev/null +++ b/contracts/testhelpers/TestWrappedNativeToken.sol @@ -0,0 +1,775 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +pragma solidity 0.5.17; + +contract TestWrappedNativeToken { + string public name = "Wrapped Native Token"; + string public symbol = "WNT"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); + event Deposit(address indexed dst, uint256 wad); + event Withdrawal(address indexed src, uint256 wad); + + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function() external payable { + deposit(); + } + + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint256) { + return address(this).balance; + } + + function approve(address guy, uint256 wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint256 wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint256 wad) public returns (bool) { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + emit Transfer(src, dst, wad); + + return true; + } + + /** + * added for local swap implementation + * */ + function mint(address _to, uint256 _value) public { + require(_to != address(0), "no burn allowed"); + balanceOf[_to] = balanceOf[_to] + _value; + emit Transfer(address(0), _to, _value); + } + + /** + * added for local swap implementation + * */ + function burn(address _who, uint256 _value) public { + require(_value <= balanceOf[_who], "balance too low"); + // no need to require _value <= totalSupply, since that would imply the + // sender's balance is greater than the totalSupply, which *should* be an assertion failure + + balanceOf[_who] = balanceOf[_who] - _value; + emit Transfer(_who, address(0), _value); + } +} + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + +*/ diff --git a/contracts/testhelpers/WrappedNativeToken.sol b/contracts/testhelpers/WrappedNativeToken.sol new file mode 100644 index 000000000..f2f960445 --- /dev/null +++ b/contracts/testhelpers/WrappedNativeToken.sol @@ -0,0 +1,754 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +pragma solidity 0.5.17; + +contract WrappedNativeToken { + string public name = "Wrapped Native Token"; + string public symbol = "WNT"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); + event Deposit(address indexed dst, uint256 wad); + event Withdrawal(address indexed src, uint256 wad); + + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function() external payable { + deposit(); + } + + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint256) { + return address(this).balance; + } + + function approve(address guy, uint256 wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint256 wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint256 wad) public returns (bool) { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + emit Transfer(src, dst, wad); + + return true; + } +} + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + +*/ diff --git a/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json b/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json new file mode 100644 index 000000000..a704097ee --- /dev/null +++ b/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json @@ -0,0 +1,1517 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "LoanTokenLogicWrbtc", + "sourceName": "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtc.sol", + "address":"0xa9DcDC63eaBb8a2b6f39D7fF9429d88340044a7A", + "implementation":"0xB6D6Ed584240308b6CF2f654aA6027A35926e129", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "valueBefore", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "valueAfter", + "type": "uint256" + } + ], + "name": "AllowanceUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "loanToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "loanAmount", + "type": "uint256" + } + ], + "name": "FlashBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "TINY_AMOUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "assetBalanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "avgBorrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "baseRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "withdrawAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "borrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "burnAmount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "loanAmountPaid", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "burnAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "useLM", + "type": "bool" + } + ], + "name": "burnToBTC", + "outputs": [ + { + "internalType": "uint256", + "name": "loanAmountPaid", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "assetBorrow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetSupply", + "type": "uint256" + } + ], + "name": "calculateSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + } + ], + "name": "checkPriceDivergence", + "outputs": [], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "checkpointPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "checkpointSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "earlyAccessToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getBorrowAmountForDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getDepositAmountForBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getEstimatedMarginDetails", + "outputs": [ + { + "internalType": "uint256", + "name": "principal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateral", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "interestRate", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getListFunctionSignatures", + "outputs": [ + { + "internalType": "bytes4[]", + "name": "functionSignatures", + "type": "bytes4[]" + }, + { + "internalType": "bytes32", + "name": "moduleName", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + } + ], + "name": "getMaxEscrowAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "maxEscrowAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kinkLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "liquidityMiningAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "loanParamsIds", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "loanTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lowUtilBaseRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lowUtilRateMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "loanDataBytes", + "type": "bytes" + } + ], + "name": "marginTrade", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "affiliateReferrer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "loanDataBytes", + "type": "bytes" + } + ], + "name": "marginTradeAffiliate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "marketLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxScaleRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bool", + "name": "useLM", + "type": "bool" + } + ], + "name": "mintWithBTC", + "outputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "nextBorrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "supplyAmount", + "type": "uint256" + } + ], + "name": "nextSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pauser", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "profitOf", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "rateMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "sovrynContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "source", + "type": "string" + } + ], + "name": "stringToBytes32", + "outputs": [ + { + "internalType": "bytes32", + "name": "result", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "supplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "targetLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "target_", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tokenPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalAssetBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalAssetSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "assetSupply", + "type": "uint256" + } + ], + "name": "totalSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "transactionLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "wrbtcTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x6080604052600160009081556200001e6001600160e01b036200007216565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35062000076565b3390565b615fd080620000866000396000f3fe6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040", + "deployedBytecode": "0x6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json b/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json new file mode 100644 index 000000000..f190de69c --- /dev/null +++ b/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json @@ -0,0 +1,1517 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "LoanTokenLogicWrbtc", + "sourceName": "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtc.sol", + "address":"0xe67Fe227e0504e8e96A34C3594795756dC26e14B", + "implementation":"0xACD9fe12Ee54f4a9e4EF6a08FF9ae9a5E46bC805", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "valueBefore", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "valueAfter", + "type": "uint256" + } + ], + "name": "AllowanceUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "burner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "loanToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "loanAmount", + "type": "uint256" + } + ], + "name": "FlashBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "TINY_AMOUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "assetBalanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "avgBorrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "baseRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "withdrawAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "borrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "burnAmount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "loanAmountPaid", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "burnAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "useLM", + "type": "bool" + } + ], + "name": "burnToBTC", + "outputs": [ + { + "internalType": "uint256", + "name": "loanAmountPaid", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "assetBorrow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetSupply", + "type": "uint256" + } + ], + "name": "calculateSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + } + ], + "name": "checkPriceDivergence", + "outputs": [], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + } + ], + "name": "checkpointPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "checkpointSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "earlyAccessToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getBorrowAmountForDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialLoanDuration", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getDepositAmountForBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + } + ], + "name": "getEstimatedMarginDetails", + "outputs": [ + { + "internalType": "uint256", + "name": "principal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateral", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "interestRate", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getListFunctionSignatures", + "outputs": [ + { + "internalType": "bytes4[]", + "name": "functionSignatures", + "type": "bytes4[]" + }, + { + "internalType": "bytes32", + "name": "moduleName", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + } + ], + "name": "getMaxEscrowAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "maxEscrowAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isOwner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kinkLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "liquidityMiningAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "loanParamsIds", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "loanTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lowUtilBaseRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lowUtilRateMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "loanDataBytes", + "type": "bytes" + } + ], + "name": "marginTrade", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "loanId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "leverageAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "loanTokenSent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralTokenSent", + "type": "uint256" + }, + { + "internalType": "address", + "name": "collateralTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minEntryPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "affiliateReferrer", + "type": "address" + }, + { + "internalType": "bytes", + "name": "loanDataBytes", + "type": "bytes" + } + ], + "name": "marginTradeAffiliate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "marketLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "maxScaleRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "depositAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "bool", + "name": "useLM", + "type": "bool" + } + ], + "name": "mintWithBTC", + "outputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "nextBorrowInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "supplyAmount", + "type": "uint256" + } + ], + "name": "nextSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "pauser", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "profitOf", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "rateMultiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "sovrynContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "source", + "type": "string" + } + ], + "name": "stringToBytes32", + "outputs": [ + { + "internalType": "bytes32", + "name": "result", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "supplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "targetLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "target_", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "tokenPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalAssetBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalAssetSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "assetSupply", + "type": "uint256" + } + ], + "name": "totalSupplyInterestRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "transactionLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "wrbtcTokenAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x6080604052600160009081556200001e6001600160e01b036200007216565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35062000076565b3390565b615fd080620000866000396000f3fe6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040", + "deployedBytecode": "0x6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/external/deployments/rskMainnet/WrappedNativeToken.json b/external/deployments/rskMainnet/WrappedNativeToken.json new file mode 100644 index 000000000..655612dc2 --- /dev/null +++ b/external/deployments/rskMainnet/WrappedNativeToken.json @@ -0,0 +1,276 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "WrappedNativeTokenERC20", + "sourceName": "contracts/interfaces/WrappedNativeTokenERC20.sol", + "address": "0x542fda317318ebf1d3deaf76e0b632741a7e677d", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/external/deployments/rskTestnet/WrappedNativeToken.json b/external/deployments/rskTestnet/WrappedNativeToken.json new file mode 100644 index 000000000..7cfa35b85 --- /dev/null +++ b/external/deployments/rskTestnet/WrappedNativeToken.json @@ -0,0 +1,276 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "WrappedNativeTokenERC20", + "sourceName": "contracts/interfaces/WrappedNativeTokenERC20.sol", + "address": "0x69FE5cEC81D5eF92600c1A0dB1F11986AB3758Ab", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/hardhat/tasks/feeSharingCollector.js b/hardhat/tasks/feeSharingCollector.js index 37bb1941e..dc6bc55da 100644 --- a/hardhat/tasks/feeSharingCollector.js +++ b/hardhat/tasks/feeSharingCollector.js @@ -5,26 +5,29 @@ const { sendWithMultisig } = require("../../deployment/helpers/helpers"); task( "feeSharingCollector:initialize", - "Initialize feeSharingCollector: set WRBTC and Loan Token WRBTC addresses to the FeeSharingCollector storage" + "Initialize feeSharingCollector: set WrappedNativeToken and Loan Token WrappedNativeToken addresses to the FeeSharingCollector storage" ) .addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer") .setAction(async ({ signer }, hre) => { await initializeFeeSharingCollector(hre, signer, true); }); -task("feeSharingCollector:setWrtbcTokenAddress", "Set WRBTC token address in feeSharingCollector") +task( + "feeSharingCollector:setWrtbcTokenAddress", + "Set WrappedNativeToken token address in feeSharingCollector" +) .addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer") .setAction(async ({ signer }, hre) => { - await setWrbtcTokenAddress(hre, signer, true); + await setWrappedNativeTokenAddress(hre, signer, true); }); task( "feeSharingCollector:setLoanTokenWrtbcAddress", - "Set WRBTC loan token address in feeSharingCollector" + "Set WrappedNativeToken loan token address in feeSharingCollector" ) .addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer") .setAction(async ({ signer }, hre) => { - await setLoanTokenWrbtcAddress(hre, signer, true); + await setLoanWrappedNativeTokenAddress(hre, signer, true); }); const initializeFeeSharingCollector = async (hre, signer) => { @@ -42,16 +45,16 @@ const initializeFeeSharingCollector = async (hre, signer) => { return; } - const wrbtcToken = (await get("WRBTC")).address; - const loanWrbtcToken = (await get("LoanToken_iRBTC")).address; + const wrappedNativeToken = (await get("WrappedNativeToken")).address; + const loanWrappedNativeToken = (await get("LoanToken_iNativeToken")).address; - if (!ethers.utils.isAddress(wrbtcToken)) { - logger.error(`WRBTC - ${wrbtcToken} is invalid address`); + if (!ethers.utils.isAddress(wrappedNativeToken)) { + logger.error(`WrappedNativeToken - ${wrappedNativeToken} is invalid address`); return; } - if (!ethers.utils.isAddress(loanWrbtcToken)) { - logger.error(`loan token iRBTC - ${loanWrbtcToken} is invalid address`); + if (!ethers.utils.isAddress(loanWrappedNativeToken)) { + logger.error(`loan token iNativeToken- ${loanWrappedNativeToken} is invalid address`); return; } @@ -60,21 +63,24 @@ const initializeFeeSharingCollector = async (hre, signer) => { const signerAcc = (await hre.getNamedAccounts())[signer]; const targetDeploymentAddress = (await get("FeeSharingCollector")).address; const iface = new ethers.utils.Interface([ - "function initialize(address wrbtcToken, address loanWrbtcToken)", + "function initialize(address wrappedNativeToken, address loanWrappedNativeToken)", + ]); + let data = await iface.encodeFunctionData("initialize", [ + wrappedNativeToken, + loanWrappedNativeToken, ]); - let data = await iface.encodeFunctionData("initialize", [wrbtcToken, loanWrbtcToken]); await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc); }; -const setWrbtcTokenAddress = async (hre, signer) => { +const setWrappedNativeTokenAddress = async (hre, signer) => { const { deployments: { get }, ethers, } = hre; - const wrbtcToken = (await get("WRBTC")).address; - if (!ethers.utils.isAddress(wrbtcToken)) { - logger.error(`wrbtcToken - ${wrbtcToken} is invalid address`); + const wrappedNativeToken = (await get("WrappedNativeToken")).address; + if (!ethers.utils.isAddress(wrappedNativeToken)) { + logger.error(`wrappedNativeToken - ${wrappedNativeToken} is invalid address`); return; } @@ -83,21 +89,21 @@ const setWrbtcTokenAddress = async (hre, signer) => { const signerAcc = (await hre.getNamedAccounts())[signer]; const targetDeploymentAddress = (await get("FeeSharingCollector")).address; const iface = new ethers.utils.Interface([ - "function setWrbtcToken(address newWrbtcTokenAddress)", + "function setWrappedNativeToken(address newWrappedNativeTokenAddress)", ]); - let data = await iface.encodeFunctionData("setWrbtcToken", [wrbtcToken]); + let data = await iface.encodeFunctionData("setWrappedNativeToken", [wrappedNativeToken]); await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc); }; -const setLoanTokenWrbtcAddress = async (hre, signer) => { +const setLoanWrappedNativeTokenAddress = async (hre, signer) => { const { deployments: { get }, ethers, } = hre; - const loanWrbtcToken = (await get("iRBTC")).address; - if (!ethers.utils.isAddress(loanWrbtcToken)) { - logger.error(`loanWrbtcToken - ${loanWrbtcToken} is invalid address`); + const loanWrappedNativeToken = (await get("LoanToken_iNativeToken")).address; + if (!ethers.utils.isAddress(loanWrappedNativeToken)) { + logger.error(`loanWrappedNativeToken - ${loanWrappedNativeToken} is invalid address`); return; } @@ -106,8 +112,10 @@ const setLoanTokenWrbtcAddress = async (hre, signer) => { const signerAcc = (await hre.getNamedAccounts())[signer]; const targetDeploymentAddress = (await get("FeeSharingCollector")).address; const iface = new ethers.utils.Interface([ - "function setLoanTokenWrbtc(address newLoanTokenWrbtcAddress)", + "function setLoanWrappedNativeToken(address newLoanWrappedNativeTokenAddress)", + ]); + let data = await iface.encodeFunctionData("setLoanWrappedNativeToken", [ + loanWrappedNativeToken, ]); - let data = await iface.encodeFunctionData("setLoanTokenWrbtc", [loanWrbtcToken]); await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc); }; diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index a511b11f2..a2146784d 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -16,7 +16,7 @@ * It didn't work. * Update to use initializer.js SUSD. * It works Ok. - * Update to use WRBTC as collateral token, instead of custom testWrbtc. + * Update to use wrappedNativeToken as collateral token, instead of custom testWrappedNativeToken. * It works Ok. * Update to use initializer.js SOV. * It didn't work. @@ -122,14 +122,14 @@ contract("FeeSharingCollector:", (accounts) => { const name = "Test SOVToken"; const symbol = "TST"; - let RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT; + let NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; let root, account1, account2, account3, account4; - let SOVToken, SUSD, WRBTC, sovryn, staking; + let SOVToken, SUSD, WrappedNativeToken, sovryn, staking; let loanTokenSettings, loanTokenLogic, loanToken; let feeSharingCollectorProxyObj; let feeSharingCollector; let feeSharingCollectorLogic; - let loanTokenWrbtc; + let loanWrappedNativeToken; let tradingFeePercent; let mockPrice; let liquidityPoolV1Converter; @@ -176,10 +176,10 @@ contract("FeeSharingCollector:", (accounts) => { iWeightedStakingModuleMockup = await IWeightedStakingModuleMockup.at(staking.address); SUSD = await getSUSD(); - RBTC = await getRBTC(); - WRBTC = await getWRBTC(); + NativeToken = await getRBTC(); + WrappedNativeToken = await getWRBTC(); BZRX = await getBZRX(); - priceFeeds = await getPriceFeeds(WRBTC, SUSD, RBTC, BZRX); + priceFeeds = await getPriceFeeds(WrappedNativeToken, SUSD, NativeToken, BZRX); // Deploying sovrynProtocol w/ generic function from initializer.js /// @dev Tried but no success so far. When using the getSovryn function @@ -187,7 +187,7 @@ contract("FeeSharingCollector:", (accounts) => { /// The weird thing is that deployment code below is exactly the same as /// the code from getSovryn function at initializer.js. /// Inline code works ok, but when calling the function it does not. - // sovryn = await getSovryn(WRBTC, SUSD, RBTC, priceFeeds); + // sovryn = await getSovryn(WrappedNativeToken, SUSD, NativeToken, priceFeeds); // await sovryn.setSovrynProtocolAddress(sovryn.address); const sovrynproxy = await Protocol.new(); @@ -198,7 +198,7 @@ contract("FeeSharingCollector:", (accounts) => { await sovryn.replaceContract((await LoanMaintenance.new()).address); await sovryn.replaceContract((await SwapsExternal.new()).address); - await sovryn.setWrbtcToken(WRBTC.address); + await sovryn.setWrbtcToken(WrappedNativeToken.address); await sovryn.replaceContract((await LoanClosingsWith.new()).address); await sovryn.replaceContract((await LoanClosingsLiquidation.new()).address); @@ -217,7 +217,7 @@ contract("FeeSharingCollector:", (accounts) => { root, loanTokenLogic.address, sovryn.address, - WRBTC.address + WrappedNativeToken.address ); await loanToken.initialize(SUSD.address, "iSUSD", "iSUSD"); @@ -241,33 +241,40 @@ contract("FeeSharingCollector:", (accounts) => { await sovryn.setFeesController(feeSharingCollector.address); - // Set loan pool for wRBTC -- because our fee sharing proxy required the loanPool of wRBTC + // Set loan pool for wrappedNativeToken -- because our fee sharing proxy required the loanPool of wrappedNativeToken // Loan token const initLoanTokenLogicWrbtc = await getLoanTokenLogicWrbtc(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon] loanTokenLogicWrbtc = initLoanTokenLogicWrbtc[0]; loanTokenLogicBeaconWrbtc = initLoanTokenLogicWrbtc[1]; - loanTokenWrbtc = await LoanToken.new( + loanWrappedNativeToken = await LoanToken.new( root, loanTokenLogicWrbtc.address, sovryn.address, - WRBTC.address + WrappedNativeToken.address + ); + await loanWrappedNativeToken.initialize( + WrappedNativeToken.address, + "iWrappedNativeToken", + "iWrappedNativeToken" ); - await loanTokenWrbtc.initialize(WRBTC.address, "iWRBTC", "iWRBTC"); /** Initialize the loan token logic proxy */ - loanTokenWrbtc = await ILoanTokenLogicProxy.at(loanTokenWrbtc.address); - await loanTokenWrbtc.setBeaconAddress(loanTokenLogicBeaconWrbtc.address); + loanWrappedNativeToken = await ILoanTokenLogicProxy.at(loanWrappedNativeToken.address); + await loanWrappedNativeToken.setBeaconAddress(loanTokenLogicBeaconWrbtc.address); /** Use interface of LoanTokenModules */ - loanTokenWrbtc = await ILoanTokenModules.at(loanTokenWrbtc.address); + loanWrappedNativeToken = await ILoanTokenModules.at(loanWrappedNativeToken.address); - const loanTokenAddressWrbtc = await loanTokenWrbtc.loanTokenAddress(); - await sovryn.setLoanPool([loanTokenWrbtc.address], [loanTokenAddressWrbtc]); + const loanTokenAddressWrappedNativeToken = await loanWrappedNativeToken.loanTokenAddress(); + await sovryn.setLoanPool( + [loanWrappedNativeToken.address], + [loanTokenAddressWrappedNativeToken] + ); - await WRBTC.mint(sovryn.address, wei("500", "ether")); + await WrappedNativeToken.mint(sovryn.address, wei("500", "ether")); - await sovryn.setWrbtcToken(WRBTC.address); + await sovryn.setWrbtcToken(WrappedNativeToken.address); await sovryn.setSOVTokenAddress(SOVToken.address); await sovryn.setSovrynProtocolAddress(sovryn.address); @@ -292,13 +299,13 @@ contract("FeeSharingCollector:", (accounts) => { ); // Set PriceFeeds - feeds = await PriceFeedsLocal.new(WRBTC.address, sovryn.address); + feeds = await PriceFeedsLocal.new(WrappedNativeToken.address, sovryn.address); mockPrice = "1"; - await feeds.setRates(SUSD.address, WRBTC.address, wei(mockPrice, "ether")); + await feeds.setRates(SUSD.address, WrappedNativeToken.address, wei(mockPrice, "ether")); const swaps = await SwapsImplSovrynSwap.new(); const sovrynSwapSimulator = await TestSovrynSwap.new(feeds.address); await sovryn.setSovrynSwapContractRegistryAddress(sovrynSwapSimulator.address); - await sovryn.setSupportedTokens([SUSD.address, WRBTC.address], [true, true]); + await sovryn.setSupportedTokens([SUSD.address, WrappedNativeToken.address], [true, true]); await sovryn.setPriceFeedContract( feeds.address // priceFeeds ); @@ -308,15 +315,18 @@ contract("FeeSharingCollector:", (accounts) => { tradingFeePercent = await sovryn.tradingFeePercent(); - await lend_btc_before_cashout(loanTokenWrbtc, new BN(wei("10", "ether")), root); + await lend_btc_before_cashout(loanWrappedNativeToken, new BN(wei("10", "ether")), root); const maxDisagreement = new BN(wei("5", "ether")); await sovryn.setMaxDisagreement(maxDisagreement); - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT = - await feeSharingCollector.RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT(); + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = + await feeSharingCollector.NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT(); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); return sovryn; } @@ -326,128 +336,167 @@ contract("FeeSharingCollector:", (accounts) => { }); describe("initialization", async () => { - it("initialize should revert if wrbtc has been set", async () => { + it("initialize should revert if wrappedNativeToken has been set", async () => { const feeSharingCollectorMock = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - await feeSharingCollectorMock.setWrbtcToken(WRBTC.address); - expect(await feeSharingCollectorMock.wrbtcTokenAddress()).to.equal(WRBTC.address); + await feeSharingCollectorMock.setWrappedNativeToken(WrappedNativeToken.address); + expect(await feeSharingCollectorMock.wrappedNativeTokenAddress()).to.equal( + WrappedNativeToken.address + ); await expectRevert( - feeSharingCollectorMock.initialize(WRBTC.address, loanTokenWrbtc.address), - "wrbtcToken or loanWrbtcToken has been initialized" + feeSharingCollectorMock.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ), + "wrappedNativeToken or loanWrappedNativeToken has been initialized" ); }); - it("initialize should revert if iWrbtc has been set", async () => { + it("initialize should revert if iWrappedNativeToken has been set", async () => { const feeSharingCollectorMock = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - await feeSharingCollectorMock.setWrbtcToken(WRBTC.address); - expect(await feeSharingCollectorMock.wrbtcTokenAddress()).to.equal(WRBTC.address); + await feeSharingCollectorMock.setWrappedNativeToken(WrappedNativeToken.address); + expect(await feeSharingCollectorMock.wrappedNativeTokenAddress()).to.equal( + WrappedNativeToken.address + ); await expectRevert( - feeSharingCollectorMock.initialize(WRBTC.address, loanTokenWrbtc.address), - "wrbtcToken or loanWrbtcToken has been initialized" + feeSharingCollectorMock.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ), + "wrappedNativeToken or loanWrappedNativeToken has been initialized" ); }); it("revert if initialize called by non-owner account", async () => { await expectRevert( - feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address, { - from: account3, - }), + feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address, + { + from: account3, + } + ), "unauthorized" ); }); - it("revert if setWrbtcToken called by non-owner account", async () => { + it("revert if setWrappedNativeToken called by non-owner account", async () => { await expectRevert( - feeSharingCollector.setWrbtcToken(WRBTC.address, { from: account3 }), + feeSharingCollector.setWrappedNativeToken(WrappedNativeToken.address, { + from: account3, + }), "unauthorized" ); }); - it("revert if setLoanTokenWrbtc called by non-owner account", async () => { + it("revert if setLoanWrappedNativeToken called by non-owner account", async () => { await expectRevert( - feeSharingCollector.setLoanTokenWrbtc(loanTokenWrbtc.address, { from: account3 }), + feeSharingCollector.setLoanWrappedNativeToken(loanWrappedNativeToken.address, { + from: account3, + }), "unauthorized" ); }); it("should revert if initialized more than once", async () => { - const wrbtcAddress = (await TestToken.new("WRBTC", "WRBTC", 18, 100)).address; - const loanTokenWrbtcAddress = (await TestToken.new("IWRBTC", "IWRBTC", 18, 100)) - .address; + const wrappedNativeTokenAddress = ( + await TestToken.new("WrappedNativeToken", "WNT", 18, 100) + ).address; + const loanWrappedNativeTokenAddress = ( + await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100) + ).address; await expectRevert( - feeSharingCollector.initialize(wrbtcAddress, loanTokenWrbtcAddress), + feeSharingCollector.initialize( + wrappedNativeTokenAddress, + loanWrappedNativeTokenAddress + ), "function can only be called once" ); }); - it("setWrbtcToken should revert if try to set non-contract address", async () => { - expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address); - let newInvalidWrbtcAddress = accounts[0]; + it("setWrappedNativeToken should revert if try to set non-contract address", async () => { + expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal( + WrappedNativeToken.address + ); + let newInvalidWrappedNativeTokenAddress = accounts[0]; await expectRevert( - feeSharingCollector.setWrbtcToken(newInvalidWrbtcAddress), - "newWrbtcTokenAddress not a contract" + feeSharingCollector.setWrappedNativeToken(newInvalidWrappedNativeTokenAddress), + "newWrappedNativeTokenAddress not a contract" ); - newInvalidWrbtcAddress = ZERO_ADDRESS; + newInvalidWrappedNativeTokenAddress = ZERO_ADDRESS; await expectRevert( - feeSharingCollector.setWrbtcToken(newInvalidWrbtcAddress), - "newWrbtcTokenAddress not a contract" + feeSharingCollector.setWrappedNativeToken(newInvalidWrappedNativeTokenAddress), + "newWrappedNativeTokenAddress not a contract" + ); + expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal( + WrappedNativeToken.address ); - expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address); }); - it("setWrbtcToken should set the wrbtc token address properly", async () => { - expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address); - const newWrbtcAddress = (await TestToken.new("WRBTC", "WRBTC", 18, 100)).address; - await feeSharingCollector.setWrbtcToken(newWrbtcAddress); - expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(newWrbtcAddress); + it("setWrappedNativeToken should set the wrappedNativeToken token address properly", async () => { + expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal( + WrappedNativeToken.address + ); + const newWrappedNativeTokenAddress = ( + await TestToken.new("WrappedNativeToken", "WNT", 18, 100) + ).address; + await feeSharingCollector.setWrappedNativeToken(newWrappedNativeTokenAddress); + expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal( + newWrappedNativeTokenAddress + ); }); - it("setLoanTokenWrbtc should revert if try to set non-contract addrerss", async () => { - expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal( - loanTokenWrbtc.address + it("setLoanWrappedNativeToken should revert if try to set non-contract addrerss", async () => { + expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal( + loanWrappedNativeToken.address ); - let newInvalidLoanTokenWrbtcAddress = accounts[0]; + let newInvalidLoanWrappedNativeTokenAddress = accounts[0]; await expectRevert( - feeSharingCollector.setLoanTokenWrbtc(newInvalidLoanTokenWrbtcAddress), - "newLoanTokenWrbtcAddress not a contract" + feeSharingCollector.setLoanWrappedNativeToken( + newInvalidLoanWrappedNativeTokenAddress + ), + "newLoanWrappedNativeTokenAddress not a contract" ); - newInvalidLoanTokenWrbtcAddress = ZERO_ADDRESS; + newInvalidLoanWrappedNativeTokenAddress = ZERO_ADDRESS; await expectRevert( - feeSharingCollector.setLoanTokenWrbtc(newInvalidLoanTokenWrbtcAddress), - "newLoanTokenWrbtcAddress not a contract" + feeSharingCollector.setLoanWrappedNativeToken( + newInvalidLoanWrappedNativeTokenAddress + ), + "newLoanWrappedNativeTokenAddress not a contract" ); - expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal( - loanTokenWrbtc.address + expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal( + loanWrappedNativeToken.address ); }); - it("setLoanTokenWrbtc should set the wrbtc token address properly", async () => { - expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal( - loanTokenWrbtc.address + it("setLoanWrappedNativeToken should set the wrappedNativeToken token address properly", async () => { + expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal( + loanWrappedNativeToken.address ); - const newLoanTokenWrbtcAddress = (await TestToken.new("IWRBTC", "IWRBTC", 18, 100)) - .address; - await feeSharingCollector.setLoanTokenWrbtc(newLoanTokenWrbtcAddress); - expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal( - newLoanTokenWrbtcAddress + const newLoanWrappedNativeTokenAddress = ( + await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100) + ).address; + await feeSharingCollector.setLoanWrappedNativeToken(newLoanWrappedNativeTokenAddress); + expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal( + newLoanWrappedNativeTokenAddress ); }); }); - describe("withdrawStartingFromCheckpoint, withdrawRBTCStartingFromCheckpoint, withdrawRbtcTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => { + describe("withdrawStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => { let snapshot; before(async () => { await loadFixture(protocolDeploymentFixture); @@ -459,11 +508,11 @@ contract("FeeSharingCollector:", (accounts) => { await snapshot.restore(); }); - // If calling withdrawStartingFromCheckpoint or withdrawRBTCStartingFromCheckpoint with _fromCheckpoint > processedCheckpoints[user][_loanPoolToken] it starts calculating the fees from _fromCheckpoint + // If calling withdrawStartingFromCheckpoint or withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint > processedCheckpoints[user][_loanPoolToken] it starts calculating the fees from _fromCheckpoint it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -522,10 +571,10 @@ contract("FeeSharingCollector:", (accounts) => { expect(processedCheckpoints.toNumber()).to.equal(10); }); - it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for rbtc & non-rbtc based tokens", async () => { + it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non-nativeToken based tokens", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -540,7 +589,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -566,7 +615,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, { @@ -574,19 +623,19 @@ contract("FeeSharingCollector:", (accounts) => { fromCheckpoint: nextPositiveSOV.checkpointNum.toNumber(), }, ], - 3, // 3 max checkpoint is enough to withdraw both RBTC & SOV Token completely + 3, // 3 max checkpoint is enough to withdraw both NativeToken * SOV Token completely ZERO_ADDRESS, { from: account1 } ); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, amount: new BN(60), }); let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); @@ -608,10 +657,10 @@ contract("FeeSharingCollector:", (accounts) => { expect(processedCheckpointsSOV.toNumber()).to.equal(10); }); - it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for rbtc & non-rbtc based tokens (withdraw partially)", async () => { + it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non-nativeToken based tokens (withdraw partially)", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -626,7 +675,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -648,7 +697,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, { @@ -656,19 +705,19 @@ contract("FeeSharingCollector:", (accounts) => { fromCheckpoint: nextPositiveSOV.checkpointNum.toNumber(), }, ], - 1, // 1 max checkpoint is only enough to withdraw RBTC + 1, // 1 max checkpoint is only enough to withdraw NativeToken ZERO_ADDRESS, { from: account1 } ); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, amount: new BN(60), }); let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); @@ -687,7 +736,7 @@ contract("FeeSharingCollector:", (accounts) => { it("withdrawStartingFromCheckpoint using claimAllCollectedFees() works with large number of unprocessed token checkpoints", async () => { // To test this, create 250 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -731,10 +780,10 @@ contract("FeeSharingCollector:", (accounts) => { expect(processedCheckpoints.toNumber()).to.equal(10); }); - it("should be able to withdraw rbtc that has skipped checkpoints using claimAllCollectedFees calculates fees correctly (using zero addreses as reciever)", async () => { - // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 + it("should be able to withdraw nativeToken that has skipped checkpoints using claimAllCollectedFees calculates fees correctly (using zero addreses as reciever)", async () => { + // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -746,7 +795,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -756,7 +805,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -765,22 +814,22 @@ contract("FeeSharingCollector:", (accounts) => { { from: account1 } ); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, amount: new BN(60), }); let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); }); - it("withraw rbtc token that has skipped checkpoints using claimAllCollectedFees() should calculates fees correctly (using actual address as receiver)", async () => { - // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 + it("withraw nativeToken token that has skipped checkpoints using claimAllCollectedFees() should calculates fees correctly (using actual address as receiver)", async () => { + // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -792,7 +841,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -802,7 +851,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -811,22 +860,22 @@ contract("FeeSharingCollector:", (accounts) => { { from: account1 } ); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, amount: new BN(60), }); let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); }); - it("withdraw rbtc tokens that has skipped checkpoints using claimAllCollectedFees() won't be processed if passed maxCheckpoints is 0", async () => { - // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 + it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() won't be processed if passed maxCheckpoints is 0", async () => { + // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -838,7 +887,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -848,7 +897,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -859,16 +908,16 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); /** Checkpoints won't be processed, stays at 0 */ expect(processedCheckpoints.toNumber()).to.equal(0); }); - it("withdraw rbtc tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non-rbtc token is passed", async () => { - // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 + it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non-nativeToken token is passed", async () => { + // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -894,14 +943,14 @@ contract("FeeSharingCollector:", (accounts) => { ZERO_ADDRESS, { from: account1 } ), - "only rbtc-based tokens are allowed" + "only nativeToken-based tokens are allowed" ); }); - it("should not be able to pass non-rbtc based token as _rbtcTokensRegularWithdraw using claimAllCollectedFees() function", async () => { - // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 + it("should not be able to pass non-nativeToken based token as _nativeTokensRegularWithdraw using claimAllCollectedFees() function", async () => { + // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 - /// RBTC + /// NativeToken await stake(900, root); const userStake = 100; @@ -927,11 +976,11 @@ contract("FeeSharingCollector:", (accounts) => { ZERO_ADDRESS, { from: account1 } ), - "only rbtc-based tokens are allowed" + "only nativeToken-based tokens are allowed" ); }); - it("getNextPositiveUserCheckpoint for RBTC returns the first checkpoint on which the user has a stake > 0", async () => { + it("getNextPositiveUserCheckpoint for NativeToken returns the first checkpoint on which the user has a stake > 0", async () => { await stake(900, root); const userStake = 100; @@ -943,7 +992,7 @@ contract("FeeSharingCollector:", (accounts) => { const nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -954,7 +1003,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectRevert( feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, 0 ), @@ -1131,7 +1180,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 0, }, ], @@ -1198,7 +1247,7 @@ contract("FeeSharingCollector:", (accounts) => { await feeSharingCollector.setUserProcessedCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 2 ); @@ -1208,7 +1257,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 2, }, ], @@ -1284,19 +1333,22 @@ contract("FeeSharingCollector:", (accounts) => { "User weighted stake should be zero at previous checkpoint" ); - // RBTC + // NativeToken for (let i = 0; i < 2; i++) { await feeSharingCollector.addCheckPoint( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, userStake ); await increaseTime(FEE_WITHDRAWAL_INTERVAL); await mineBlock(); } - await feeSharingCollector.addCheckPoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, userStake); + await feeSharingCollector.addCheckPoint( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + userStake + ); await feeSharingCollector.setUserProcessedCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); await expectRevert( @@ -1305,7 +1357,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 2, }, ], @@ -1434,18 +1486,21 @@ contract("FeeSharingCollector:", (accounts) => { ]).to.eql([10, true, true]); }); - it("getNextPositiveUserCheckpoint for RBTC returns correct [checkpointNum, hasSkippedCheckpoints, hasFees]", async () => { + it("getNextPositiveUserCheckpoint for NativeToken returns correct [checkpointNum, hasSkippedCheckpoints, hasFees]", async () => { feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); await sovryn.setFeesController(feeSharingCollector.address); let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1461,7 +1516,7 @@ contract("FeeSharingCollector:", (accounts) => { nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1473,13 +1528,13 @@ contract("FeeSharingCollector:", (accounts) => { await feeSharingCollector.setUserProcessedCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 9 ); nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1492,7 +1547,7 @@ contract("FeeSharingCollector:", (accounts) => { // undo mock user processed checkpoints await feeSharingCollector.setUserProcessedCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); @@ -1502,7 +1557,7 @@ contract("FeeSharingCollector:", (accounts) => { nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1546,20 +1601,20 @@ contract("FeeSharingCollector:", (accounts) => { .equal(0); await feeSharingCollector.setTotalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 5 ); expect( ( await feeSharingCollector.totalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ) ).toNumber() ) .equal( ( await feeSharingCollector.numTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ) ).toNumber() ) @@ -1617,7 +1672,7 @@ contract("FeeSharingCollector:", (accounts) => { it("Withdraw zero amount will success with the proper emitted event", async () => { await protocolDeploymentFixture(); const tx = await feeSharingCollector.withdrawFees([SUSD.address]); - expectEvent(tx, "FeeWithdrawnInRBTC", { + expectEvent(tx, "FeeWithdrawnInNativeToken", { sender: root, amount: new BN(0), }); @@ -1644,21 +1699,25 @@ contract("FeeSharingCollector:", (accounts) => { tradingFeeTokensHeld, borrowingFeeTokensHeld ); - let previousProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address); + let previousProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); await protocol.setFeesController(root); let tx = await protocol.withdrawFees([SUSD.address], root); - let latestProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address); + let latestProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); await checkWithdrawFee(); - //check wrbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let userBalance = await WRBTC.balanceOf.call(root); + //check wrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let userBalance = await WrappedNativeToken.balanceOf.call(root); expect(userBalance.toString()).to.be.equal(feeAmount.toString()); - // wrbtc balance should remain the same - expect(previousProtocolWrbtcBalance.toString()).to.equal( - latestProtocolWrbtcBalance.toString() + // wrappedNativeToken balance should remain the same + expect(previousProtocolWrappedNativeTokenBalance.toString()).to.equal( + latestProtocolWrappedNativeTokenBalance.toString() ); expectEvent(tx, "WithdrawFees", { @@ -1668,11 +1727,11 @@ contract("FeeSharingCollector:", (accounts) => { lendingAmount: lendingFeeTokensHeld, tradingAmount: tradingFeeTokensHeld, borrowingAmount: borrowingFeeTokensHeld, - // amountConvertedToWRBTC + // amountConvertedToWrappedNativeToken }); }); - it("ProtocolSettings.withdrawFees (WRBTC token)", async () => { + it("ProtocolSettings.withdrawFees (WrappedNativeToken token)", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -1696,22 +1755,22 @@ contract("FeeSharingCollector:", (accounts) => { ); // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); await sovryn.setFeesController(root); - let tx = await sovryn.withdrawFees([WRBTC.address], account1); + let tx = await sovryn.withdrawFees([WrappedNativeToken.address], account1); await checkWithdrawFee(true, true, false); - //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let userBalance = await WRBTC.balanceOf.call(account1); + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let userBalance = await WrappedNativeToken.balanceOf.call(account1); expect(userBalance.toString()).to.be.equal(feeAmount.toString()); expectEvent(tx, "WithdrawFees", { sender: root, - token: WRBTC.address, + token: WrappedNativeToken.address, receiver: account1, lendingAmount: lendingFeeTokensHeld, tradingAmount: tradingFeeTokensHeld, borrowingAmount: borrowingFeeTokensHeld, - wRBTCConverted: new BN(feeAmount), + wrappedNativeTokenConverted: new BN(feeAmount), }); }); @@ -1755,8 +1814,10 @@ contract("FeeSharingCollector:", (accounts) => { tradingFeeTokensHeld, borrowingFeeTokensHeld ); - let previousProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address); - let previousFeeSharingCollectorProxyRBTCBalance = new BN( + let previousProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); @@ -1764,42 +1825,45 @@ contract("FeeSharingCollector:", (accounts) => { await checkWithdrawFee(); - //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + //check iNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); // feeSharingCollector no longer provides the liquidity to lending pool. expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); - // wrbtc balance should remain the same - let latestProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address); - expect(previousProtocolWrbtcBalance.toString()).to.equal( - latestProtocolWrbtcBalance.toString() + // wrappedNativeToken balance should remain the same + let latestProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); + expect(previousProtocolWrappedNativeTokenBalance.toString()).to.equal( + latestProtocolWrappedNativeTokenBalance.toString() ); - // rbtc balance of feeSharingCollector should be increased - let latestFeeSharingCollectorProxyRBTCBalance = new BN( + // nativeToken balance of feeSharingCollector should be increased + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); expect( - previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString() - ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString()); + previousFeeSharingCollectorProxyNativeTokenBalance + .add(new BN(feeAmount)) + .toString() + ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -1809,18 +1873,18 @@ contract("FeeSharingCollector:", (accounts) => { expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); - expectEvent(tx, "FeeWithdrawnInRBTC", { + expectEvent(tx, "FeeWithdrawnInNativeToken", { sender: root, amount: feeAmount, }); }); - it("Should be able to withdraw fees (WRBTC token)", async () => { + it("Should be able to withdraw fees (WrappedNativeToken token)", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -1842,43 +1906,44 @@ contract("FeeSharingCollector:", (accounts) => { true ); - let previousFeeSharingCollectorProxyRBTCBalance = new BN( + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); - tx = await feeSharingCollector.withdrawFees([WRBTC.address]); + tx = await feeSharingCollector.withdrawFees([WrappedNativeToken.address]); await checkWithdrawFee(); - //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + //check iNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); - // rbtc balance of feeSharingCollector should be increased - let latestFeeSharingCollectorProxyRBTCBalance = new BN( + // nativeToken balance of feeSharingCollector should be increased + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); expect( - previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString() - ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString()); + previousFeeSharingCollectorProxyNativeTokenBalance + .add(new BN(feeAmount)) + .toString() + ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -1889,12 +1954,12 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); - expectEvent(tx, "FeeWithdrawnInRBTC", { + expectEvent(tx, "FeeWithdrawnInNativeToken", { sender: root, amount: feeAmount, }); @@ -1923,7 +1988,7 @@ contract("FeeSharingCollector:", (accounts) => { true ); - let previousFeeSharingCollectorProxyRBTCBalance = new BN( + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); @@ -1931,26 +1996,25 @@ contract("FeeSharingCollector:", (accounts) => { await checkWithdrawFee(false, false, true); - //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) let feeSharingCollectorProxyBalance = await SOVToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString()); - // special for SOV token, it won't be converted into rbtc, instead it will directly transfer SOV to feeSharingCollector. - // so the rbtc balance should remain the same. - let latestFeeSharingCollectorProxyRBTCBalance = new BN( + // special for SOV token, it won't be converted into nativeToken, instead it will directly transfer SOV to feeSharingCollector. + // so the nativeToken balance should remain the same. + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); - expect(previousFeeSharingCollectorProxyRBTCBalance.toString()).to.equal( - latestFeeSharingCollectorProxyRBTCBalance.toString() + expect(previousFeeSharingCollectorProxyNativeTokenBalance.toString()).to.equal( + latestFeeSharingCollectorProxyNativeTokenBalance.toString() ); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); @@ -2018,19 +2082,19 @@ contract("FeeSharingCollector:", (accounts) => { await checkWithdrawFee(); - // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); // checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -2041,7 +2105,7 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -2076,8 +2140,8 @@ contract("FeeSharingCollector:", (accounts) => { // Need to checkwithdrawfee manually await checkWithdrawFee(); - // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); @@ -2110,22 +2174,22 @@ contract("FeeSharingCollector:", (accounts) => { await increaseTime(FEE_WITHDRAWAL_INTERVAL); tx = await feeSharingCollector.withdrawFees([SUSD.address]); - // In this state the price of SUSD/WRBTC already adjusted because of previous swap, so we need to consider this in the next swapFee calculation + // In this state the price of SUSD/WrappedNativeToken already adjusted because of previous swap, so we need to consider this in the next swapFee calculation await checkWithdrawFee(); - // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); // checkpoints totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -2138,16 +2202,15 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); }); @@ -2277,10 +2340,10 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => { + it("Shouldn't be able to withdraw without checkpoints (for wrappedNativeToken pool)", async () => { await protocolDeploymentFixture(); await expectRevert( - feeSharingCollector.withdraw(loanTokenWrbtc.address, 0, account2, { + feeSharingCollector.withdraw(loanWrappedNativeToken.address, 0, account2, { from: account1, }), "FeeSharingCollector::withdraw: _maxCheckpoints should be positive" @@ -2300,7 +2363,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2309,7 +2372,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectRevert( feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 0 ), @@ -2327,7 +2390,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2339,7 +2402,7 @@ contract("FeeSharingCollector:", (accounts) => { const allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2435,7 +2498,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(allUserFees[0]).to.equal(0); }); - it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with 1 iteration", async () => { + it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with 1 iteration", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -2455,12 +2518,12 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, 10000000 ); @@ -2470,7 +2533,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2483,13 +2546,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1000 ); @@ -2497,7 +2560,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(allUserFees.length).to.equal(0); }); - it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (1 maxCheckpoint)", async () => { + it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (1 maxCheckpoint)", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -2519,7 +2582,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2529,7 +2592,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2540,7 +2603,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2550,7 +2613,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2563,13 +2626,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2577,7 +2640,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(allUserFees.length).to.equal(0); }); - it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (2 maxCheckpoint)", async () => { + it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (2 maxCheckpoint)", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -2599,7 +2662,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2609,7 +2672,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2620,7 +2683,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2630,7 +2693,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2643,13 +2706,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2657,7 +2720,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(allUserFees.length).to.equal(0); }); - it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (3 maxCheckpoint)", async () => { + it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (3 maxCheckpoint)", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -2679,7 +2742,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2689,7 +2752,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2700,7 +2763,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2710,7 +2773,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2723,13 +2786,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2737,7 +2800,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(allUserFees.length).to.equal(0); }); - it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - starting from > 0 checkpoints", async () => { + it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - starting from > 0 checkpoints", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -2759,7 +2822,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2769,7 +2832,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2780,7 +2843,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2790,7 +2853,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 2, ZERO_ADDRESS, @@ -2803,13 +2866,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(2); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, 100 ); @@ -2823,7 +2886,7 @@ contract("FeeSharingCollector:", (accounts) => { } allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, tempMaxCheckpoint ); @@ -2837,7 +2900,7 @@ contract("FeeSharingCollector:", (accounts) => { } allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, tempMaxCheckpoint ); @@ -2997,33 +3060,33 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => { + it("Shouldn't be able to withdraw zero amount (for wrappedNativeToken pool)", async () => { await protocolDeploymentFixture(); let fees = await feeSharingCollector.getAccumulatedFees( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); expect(fees).to.be.bignumber.equal("0"); await expectRevert( - feeSharingCollector.withdraw(loanTokenWrbtc.address, 10, ZERO_ADDRESS, { + feeSharingCollector.withdraw(loanWrappedNativeToken.address, 10, ZERO_ADDRESS, { from: account1, }), "FeeSharingCollector::withdrawFees: no tokens for withdrawal" ); }); - it("Shouldn't be able to withdraw zero amount (for wRBTC pool) - using claimAllCollectedFees()", async () => { + it("Shouldn't be able to withdraw zero amount (for wrappedNativeToken pool) - using claimAllCollectedFees()", async () => { await protocolDeploymentFixture(); let fees = await feeSharingCollector.getAccumulatedFees( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); expect(fees).to.be.bignumber.equal("0"); await expectRevert( feeSharingCollector.claimAllCollectedFees( - [loanTokenWrbtc.address], + [loanWrappedNativeToken.address], [], [], 10, @@ -3036,7 +3099,7 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Should not be able to pass non-rbtc based token as _rbtcTokensRegularWithdraw in claimAllCollectedFees() function", async () => { + it("Should not be able to pass non-nativeToken based token as _nativeTokensRegularWithdraw in claimAllCollectedFees() function", async () => { await protocolDeploymentFixture(); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3066,7 +3129,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); @@ -3081,7 +3144,7 @@ contract("FeeSharingCollector:", (accounts) => { from: account1, } ), - "only rbtc-based tokens are allowed" + "only nativeToken-based tokens are allowed" ); }); @@ -3115,13 +3178,13 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3131,24 +3194,30 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = - await Promise.all([ - feeSharingCollector.processedCheckpoints.call( - account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT - ), - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call( - account1, - loanTokenWrbtc.address - ), - ]); + let [ + processedCheckpointsNativeToken, + processedCheckpointsWrappedNativeToken, + processedCheckpointsIWrappedNativeToken, + ] = await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ), + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(0); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(0); + expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(0); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(0); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), @@ -3216,7 +3285,7 @@ contract("FeeSharingCollector:", (accounts) => { }); }); - it("Should be able to withdraw reegular rbtc token to another account using claimAllCollectedFees()", async () => { + it("Should be able to withdraw reegular nativeToken token to another account using claimAllCollectedFees()", async () => { await protocolDeploymentFixture(); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3246,13 +3315,13 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3262,31 +3331,37 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = - await Promise.all([ - feeSharingCollector.processedCheckpoints.call( - account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT - ), - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call( - account1, - loanTokenWrbtc.address - ), - ]); + let [ + processedCheckpointsNativeToken, + processedCheckpointsWrappedNativeToken, + processedCheckpointsIWrappedNativeToken, + ] = await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ), + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(0); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(0); + expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(0); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(0); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), }); }); - it("Should be able to withdraw to another account (WRBTC) - using claimAllCollectedFees()", async () => { + it("Should be able to withdraw to another account (WrappedNativeToken) - using claimAllCollectedFees()", async () => { await protocolDeploymentFixture(); // FeeSharingCollectorProxy @@ -3295,10 +3370,13 @@ contract("FeeSharingCollector:", (accounts) => { staking.address ); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); await sovryn.setFeesController(feeSharingCollector.address); - await WRBTC.mint(feeSharingCollector.address, wei("2", "ether")); + await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether")); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3325,49 +3403,57 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld ); - /** Add checkpoint for WRBTC */ - await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString()); + /** Add checkpoint for WrappedNativeToken */ + await feeSharingCollector.addCheckPoint( + WrappedNativeToken.address, + totalFeeTokensHeld.toString() + ); - /** Add checkpoint for iWRBTC */ + /** Add checkpoint for iWNT */ await feeSharingCollector.addCheckPoint( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, totalFeeTokensHeld.toString() ); - await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, { + await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, { value: totalFeeTokensHeld, }); await feeSharingCollector.withdrawFees([SUSD.address]); - const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); - const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call( - account1, - WRBTC.address - ); - const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesWrappedNativeToken = + await feeSharingCollector.getAccumulatedFees.call( + account1, + WrappedNativeToken.address + ); + const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString()); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString()); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesWrappedNativeToken.toString() + ); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesINativeToken.toString() + ); let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) ); - /** Withdraw RBTC */ + /** Withdraw NativeToken */ let tx1 = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], + [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3375,10 +3461,10 @@ contract("FeeSharingCollector:", (accounts) => { from: account1, } ); - /** Withdraw WRBTC */ + /** Withdraw WrappedNativeToken */ let tx2 = await feeSharingCollector.claimAllCollectedFees( [], - [WRBTC.address], + [WrappedNativeToken.address], [], 1000, account2, @@ -3387,10 +3473,10 @@ contract("FeeSharingCollector:", (accounts) => { } ); - /** Withdraw IWRBTC */ + /** Withdraw INativeToken */ let tx3 = await feeSharingCollector.claimAllCollectedFees( [], - [loanTokenWrbtc.address], + [loanWrappedNativeToken.address], [], 1000, account2, @@ -3400,49 +3486,55 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = - await Promise.all([ - feeSharingCollector.processedCheckpoints.call( - account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT - ), - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call( - account1, - loanTokenWrbtc.address - ), - ]); + let [ + processedCheckpointsNativeToken, + processedCheckpointsWrappedNativeToken, + processedCheckpointsIWrappedNativeToken, + ] = await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ), + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1); + expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1); - expectEvent(tx1, "RBTCWithdrawn", { + expectEvent(tx1, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) .mul(new BN(userStakePercentage).mul(new BN(1))) - .div(new BN(10)), // need multiple by 1 only since we only withdraw RBTC in tx1 + .div(new BN(10)), // need multiple by 1 only since we only withdraw NativeToken in tx1 }); - expectEvent(tx2, "RBTCWithdrawn", { + expectEvent(tx2, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) .mul(new BN(userStakePercentage).mul(new BN(1))) - .div(new BN(10)), // need multiple by 1 only since we only withdraw WRBTC in tx2 + .div(new BN(10)), // need multiple by 1 only since we only withdraw WrappedNativeToken in tx2 }); - expectEvent(tx3, "RBTCWithdrawn", { + expectEvent(tx3, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) .mul(new BN(userStakePercentage).mul(new BN(1))) - .div(new BN(10)), // need multiple by 1 only since we only withdraw IWRBTC in tx3 + .div(new BN(10)), // need multiple by 1 only since we only withdraw INativeToken in tx3 }); }); - it("Should be able to withdraw to another account (WRBTC) - using claimAllCollectedFees() - Within 1 transaction", async () => { + it("Should be able to withdraw to another account (WrappedNativeToken) - using claimAllCollectedFees() - Within 1 transaction", async () => { await protocolDeploymentFixture(); // FeeSharingCollectorProxy @@ -3451,10 +3543,13 @@ contract("FeeSharingCollector:", (accounts) => { staking.address ); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); await sovryn.setFeesController(feeSharingCollector.address); - await WRBTC.mint(feeSharingCollector.address, wei("2", "ether")); + await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether")); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3481,49 +3576,61 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld ); - /** Add checkpoint for WRBTC */ - await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString()); + /** Add checkpoint for WrappedNativeToken */ + await feeSharingCollector.addCheckPoint( + WrappedNativeToken.address, + totalFeeTokensHeld.toString() + ); - /** Add checkpoint for iWRBTC */ + /** Add checkpoint for iWNT */ await feeSharingCollector.addCheckPoint( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, totalFeeTokensHeld.toString() ); - await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, { + await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, { value: totalFeeTokensHeld, }); await feeSharingCollector.withdrawFees([SUSD.address]); - const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); - const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call( - account1, - WRBTC.address - ); - const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesWrappedNativeToken = + await feeSharingCollector.getAccumulatedFees.call( + account1, + WrappedNativeToken.address + ); + const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString()); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString()); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesWrappedNativeToken.toString() + ); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesINativeToken.toString() + ); let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) ); - /** Withdraw RBTC WRBTC & IWRBTC */ + /** Withdraw NativeToken WrappedNativeToken & INativeToken */ let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 1000, account2, @@ -3533,24 +3640,30 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = - await Promise.all([ - feeSharingCollector.processedCheckpoints.call( - account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT - ), - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call( - account1, - loanTokenWrbtc.address - ), - ]); + let [ + processedCheckpointsNativeToken, + processedCheckpointsWrappedNativeToken, + processedCheckpointsIWrappedNativeToken, + ] = await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ), + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1); + expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) @@ -3559,7 +3672,7 @@ contract("FeeSharingCollector:", (accounts) => { }); }); - it("Should be able to withdraw rbtc token related - using withdraw() function", async () => { + it("Should be able to withdraw nativeToken token related - using withdraw() function", async () => { await protocolDeploymentFixture(); // FeeSharingCollectorProxy @@ -3568,10 +3681,13 @@ contract("FeeSharingCollector:", (accounts) => { staking.address ); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); await sovryn.setFeesController(feeSharingCollector.address); - await WRBTC.mint(feeSharingCollector.address, wei("2", "ether")); + await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether")); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3598,53 +3714,66 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld ); - /** Add checkpoint for WRBTC */ - await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString()); + /** Add checkpoint for WrappedNativeToken */ + await feeSharingCollector.addCheckPoint( + WrappedNativeToken.address, + totalFeeTokensHeld.toString() + ); - /** Add checkpoint for iWRBTC */ + /** Add checkpoint for iWNT */ await feeSharingCollector.addCheckPoint( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, totalFeeTokensHeld.toString() ); - await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, { + await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, { value: totalFeeTokensHeld, }); await feeSharingCollector.withdrawFees([SUSD.address]); - const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); - const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call( - account1, - WRBTC.address - ); - const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesWrappedNativeToken = + await feeSharingCollector.getAccumulatedFees.call( + account1, + WrappedNativeToken.address + ); + const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString()); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString()); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesWrappedNativeToken.toString() + ); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesINativeToken.toString() + ); let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) ); - /** Withdraw WRBTC */ - let tx2 = await feeSharingCollector.trueWithdraw(WRBTC.address, 1000, account2, { - from: account1, - }); + /** Withdraw WrappedNativeToken */ + let tx2 = await feeSharingCollector.trueWithdraw( + WrappedNativeToken.address, + 1000, + account2, + { + from: account1, + } + ); - /** Withdraw IWRBTC */ + /** Withdraw INativeToken */ let tx3 = await feeSharingCollector.trueWithdraw( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, 1000, account2, { @@ -3653,18 +3782,25 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = await Promise.all([ - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call(account1, loanTokenWrbtc.address), - ]); + let [processedCheckpointsWrappedNativeToken, processedCheckpointsIWrappedNativeToken] = + await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1); expectEvent(tx2, "UserFeeWithdrawn", { sender: account1, receiver: account2, - token: WRBTC.address, + token: WrappedNativeToken.address, amount: new BN(feeAmount) .mul(new BN(userStakePercentage).mul(new BN(1))) .div(new BN(10)), @@ -3673,14 +3809,14 @@ contract("FeeSharingCollector:", (accounts) => { expectEvent(tx3, "UserFeeWithdrawn", { sender: account1, receiver: account2, - token: loanTokenWrbtc.address, + token: loanWrappedNativeToken.address, amount: new BN(feeAmount) .mul(new BN(userStakePercentage).mul(new BN(1))) .div(new BN(10)), }); }); - it("Should be able to withdraw to another account (WRBTC) - using withdrawRbtcToken() - Within 1 transaction partially", async () => { + it("Should be able to withdraw to another account (WrappedNativeToken) - using withdrawNativeToken() - Within 1 transaction partially", async () => { await protocolDeploymentFixture(); // FeeSharingCollectorProxy @@ -3689,10 +3825,13 @@ contract("FeeSharingCollector:", (accounts) => { staking.address ); - await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address + ); await sovryn.setFeesController(feeSharingCollector.address); - await WRBTC.mint(feeSharingCollector.address, wei("2", "ether")); + await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether")); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3719,51 +3858,63 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld ); - /** Add checkpoint for WRBTC */ - await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString()); + /** Add checkpoint for WrappedNativeToken */ + await feeSharingCollector.addCheckPoint( + WrappedNativeToken.address, + totalFeeTokensHeld.toString() + ); - /** Add checkpoint for iWRBTC */ + /** Add checkpoint for iWNT */ await feeSharingCollector.addCheckPoint( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, totalFeeTokensHeld.toString() ); - await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, { + await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, { value: totalFeeTokensHeld, }); await feeSharingCollector.withdrawFees([SUSD.address]); - const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); - const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call( - account1, - WRBTC.address - ); - const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call( + const accumulatedFeesWrappedNativeToken = + await feeSharingCollector.getAccumulatedFees.call( + account1, + WrappedNativeToken.address + ); + const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - loanTokenWrbtc.address + loanWrappedNativeToken.address ); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString()); - expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString()); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesWrappedNativeToken.toString() + ); + expect(accumulatedFeesNativeToken.toString()).to.equal( + accumulatedFeesINativeToken.toString() + ); let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) ); - /** Withdraw RBTC WRBTC & IWRBTC */ - /** @note IWRBTC won't be withdrawn here because we only pass 2 as max checkpoints */ - /** Only RBTC & WRBTC will be withdrawn */ + /** Withdraw NativeToken WrappedNativeToken & INativeToken */ + /** @note INativeToken won't be withdrawn here because we only pass 2 as max checkpoints */ + /** Only NativeToken * WrappedNativeToken will be withdrawn */ let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 2, account2, @@ -3772,10 +3923,14 @@ contract("FeeSharingCollector:", (accounts) => { } ); - /** In this tx, it will withdraw IWRBTC only, since RBTC & WRBTC has no more checkpoints to be withdrawn */ + /** In this tx, it will withdraw INativeToken only, since NativeToken * WrappedNativeToken has no more checkpoints to be withdrawn */ let tx2 = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 1, account2, @@ -3785,24 +3940,30 @@ contract("FeeSharingCollector:", (accounts) => { ); // processedCheckpoints - let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = - await Promise.all([ - feeSharingCollector.processedCheckpoints.call( - account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT - ), - feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address), - feeSharingCollector.processedCheckpoints.call( - account1, - loanTokenWrbtc.address - ), - ]); + let [ + processedCheckpointsNativeToken, + processedCheckpointsWrappedNativeToken, + processedCheckpointsIWrappedNativeToken, + ] = await Promise.all([ + feeSharingCollector.processedCheckpoints.call( + account1, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ), + feeSharingCollector.processedCheckpoints.call( + account1, + WrappedNativeToken.address + ), + feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ), + ]); - expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1); - expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1); + expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1); + expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) @@ -3810,7 +3971,7 @@ contract("FeeSharingCollector:", (accounts) => { .div(new BN(10)), }); - expectEvent(tx2, "RBTCWithdrawn", { + expectEvent(tx2, "NativeTokenWithdrawn", { sender: account1, receiver: account2, amount: new BN(feeAmount) @@ -3819,7 +3980,7 @@ contract("FeeSharingCollector:", (accounts) => { }); }); - it("Should be able to withdraw (WRBTC pool) using claimAllCollectedFees()", async () => { + it("Should be able to withdraw (WrappedNativeToken pool) using claimAllCollectedFees()", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -3851,14 +4012,18 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 30, ZERO_ADDRESS, @@ -3901,24 +4066,24 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(1); // check balances - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0); - let userLoanTokenBalance = await loanTokenWrbtc.balanceOf.call(account1); + let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1); expect(userLoanTokenBalance.toNumber()).to.be.equal(0); let userExpectedBtcBalance = userInitialBtcBalance.add( feeAmount.mul(new BN(3)).div(new BN(10)) ); expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString()); - expectEvent(tx, "RBTCWithdrawn", { + expectEvent(tx, "NativeTokenWithdrawn", { sender: account1, receiver: account1, amount: feeAmount.mul(new BN(3)).div(new BN(10)), @@ -4237,7 +4402,11 @@ contract("FeeSharingCollector:", (accounts) => { let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 1, ZERO_ADDRESS, @@ -4264,16 +4433,16 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(1); // check balances - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0); - let userBalance = await loanTokenWrbtc.balanceOf.call(account1); + let userBalance = await loanWrappedNativeToken.balanceOf.call(account1); expect(userBalance.toNumber()).to.be.equal(0); expect(userLatestBTCBalance.toString()).to.be.equal( @@ -4294,7 +4463,7 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld2 ); totalFeeAmount = totalFeeAmount.add(feeAmount); - let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount; + let totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1 = feeAmount; await increaseTime(FEE_WITHDRAWAL_INTERVAL); await feeSharingCollector.withdrawFees([SUSD.address]); @@ -4312,8 +4481,8 @@ contract("FeeSharingCollector:", (accounts) => { borrowingFeeTokensHeld3 ); totalFeeAmount = totalFeeAmount.add(feeAmount); - totalLoanTokenWRBTCBalanceShouldBeAccount1 = - totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount); + totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1 = + totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1.add(feeAmount); await increaseTime(FEE_WITHDRAWAL_INTERVAL); await feeSharingCollector.withdrawFees([SUSD.address]); @@ -4321,7 +4490,11 @@ contract("FeeSharingCollector:", (accounts) => { userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 2, ZERO_ADDRESS, @@ -4338,23 +4511,27 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(3); // check balances - feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0); - userBalance = await loanTokenWrbtc.balanceOf.call(account1); + userBalance = await loanWrappedNativeToken.balanceOf.call(account1); expect(userBalance.toNumber()).to.be.equal(0); userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); expect(userLatestBTCBalance.toString()).to.be.equal( userInitialBtcBalance - .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10))) + .add( + totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1 + .mul(new BN(1)) + .div(new BN(10)) + ) .toString() ); }); @@ -4377,7 +4554,11 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 1000, ZERO_ADDRESS, @@ -4389,7 +4570,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); }); @@ -4412,7 +4593,11 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 5, ZERO_ADDRESS, @@ -4424,13 +4609,17 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(5); tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 3, ZERO_ADDRESS, @@ -4442,13 +4631,17 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(8); tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 1000, ZERO_ADDRESS, @@ -4460,7 +4653,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); }); @@ -4562,7 +4755,11 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address], + [ + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + WrappedNativeToken.address, + loanWrappedNativeToken.address, + ], [], 10, ZERO_ADDRESS, @@ -4645,7 +4842,7 @@ contract("FeeSharingCollector:", (accounts) => { let feesWithdrawn = tx.logs[1].args.amount; let userFees = await feeSharingCollector.getAccumulatedFees( account1, - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); // 100% of the fees should go to the user -> vesting contract not considered @@ -4779,41 +4976,42 @@ contract("FeeSharingCollector:", (accounts) => { "unauthorized" ); await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address); - await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address); - await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether")); + await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address); + await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether")); - let previousFeeSharingCollectorProxyRBTCBalance = new BN( + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]); - //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); - // rbtc balance of feeSharingCollector should be increased - let latestFeeSharingCollectorProxyRBTCBalance = new BN( + // nativeToken balance of feeSharingCollector should be increased + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); expect( - previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString() - ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString()); + previousFeeSharingCollectorProxyNativeTokenBalance + .add(new BN(feeAmount)) + .toString() + ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal("0"); + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal("0"); //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -4824,7 +5022,7 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -4865,43 +5063,44 @@ contract("FeeSharingCollector:", (accounts) => { "unauthorized" ); await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address); - await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address); - await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether")); + await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address); + await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether")); - let previousFeeSharingCollectorProxyRBTCBalance = new BN( + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]); - //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); - // rbtc balance of feeSharingCollector should be increased - let latestFeeSharingCollectorProxyRBTCBalance = new BN( + // nativeToken balance of feeSharingCollector should be increased + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( await web3.eth.getBalance(feeSharingCollector.address) ); expect( - previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString() - ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString()); + previousFeeSharingCollectorProxyNativeTokenBalance + .add(new BN(feeAmount)) + .toString() + ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -4912,7 +5111,7 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -4952,32 +5151,31 @@ contract("FeeSharingCollector:", (accounts) => { "unauthorized" ); await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address); - await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address); - await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether")); + await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address); + await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether")); tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]); - //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call( + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( feeSharingCollector.address ); expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString()); - // make sure wrbtc balance is 0 after withdrawal - let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal( + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( new BN(0).toString() ); //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - loanTokenWrbtc.address + loanWrappedNativeToken.address ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(0); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - loanTokenWrbtc.address, + loanWrappedNativeToken.address, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(0); @@ -4986,30 +5184,30 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - loanTokenWrbtc.address + loanWrappedNativeToken.address ); expect(lastFeeWithdrawalTime.toString()).to.be.equal("0"); }); }); - describe("withdraw wrbtc", async () => { - it("Withdraw wrbtc from non owner should revert", async () => { + describe("withdraw wrappedNativeToken", async () => { + it("Withdraw wrappedNativeToken from non owner should revert", async () => { await protocolDeploymentFixture(); const receiver = accounts[1]; - const previousBalanceReceiver = await WRBTC.balanceOf(receiver); + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); await expectRevert( - feeSharingCollector.withdrawWRBTC(receiver, 0, { from: accounts[1] }), + feeSharingCollector.withdrawWrappedNativeToken(receiver, 0, { from: accounts[1] }), "unauthorized" ); }); - it("Withdraw 0 wrbtc", async () => { + it("Withdraw 0 wrappedNativeToken", async () => { await protocolDeploymentFixture(); const receiver = accounts[1]; - const previousBalanceReceiver = await WRBTC.balanceOf(receiver); - await feeSharingCollector.withdrawWRBTC(receiver, 0); - const latestBalanceReceiver = await WRBTC.balanceOf(receiver); - const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + await feeSharingCollector.withdrawWrappedNativeToken(receiver, 0); + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); @@ -5019,28 +5217,28 @@ contract("FeeSharingCollector:", (accounts) => { expect(latestBalanceFeeSharingCollectorProxy.toString()).to.equal("0"); }); - it("Withdraw wrbtc more than the balance of feeSharingCollector should revert", async () => { + it("Withdraw wrappedNativeToken more than the balance of feeSharingCollector should revert", async () => { await protocolDeploymentFixture(); - await WRBTC.mint(root, wei("500", "ether")); - await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); const receiver = accounts[1]; - const previousBalanceReceiver = await WRBTC.balanceOf(receiver); - const feeSharingCollectorProxyBalance = await WRBTC.balanceOf( + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); const amount = feeSharingCollectorProxyBalance.add(new BN(100)); - const previousBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const previousBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); await expectRevert( - feeSharingCollector.withdrawWRBTC(receiver, amount.toString()), + feeSharingCollector.withdrawWrappedNativeToken(receiver, amount.toString()), "Insufficient balance" ); - const latestBalanceReceiver = await WRBTC.balanceOf(receiver); - const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); @@ -5052,24 +5250,24 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Fully Withdraw wrbtc", async () => { + it("Fully Withdraw wrappedNativeToken", async () => { await protocolDeploymentFixture(); - await WRBTC.mint(root, wei("500", "ether")); - await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); const receiver = accounts[1]; - const previousBalanceReceiver = await WRBTC.balanceOf(receiver); - const feeSharingCollectorProxyBalance = await WRBTC.balanceOf( + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); - const tx = await feeSharingCollector.withdrawWRBTC( + const tx = await feeSharingCollector.withdrawWrappedNativeToken( receiver, feeSharingCollectorProxyBalance.toString() ); await expectEvent.inTransaction( tx.receipt.rawLogs[0].transactionHash, - WRBTC, + WrappedNativeToken, "Transfer", { src: feeSharingCollector.address, @@ -5078,8 +5276,8 @@ contract("FeeSharingCollector:", (accounts) => { } ); - const latestBalanceReceiver = await WRBTC.balanceOf(receiver); - const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); @@ -5089,27 +5287,30 @@ contract("FeeSharingCollector:", (accounts) => { expect(latestBalanceFeeSharingCollectorProxy.toString()).to.equal("0"); }); - it("Partially Withdraw wrbtc", async () => { + it("Partially Withdraw wrappedNativeToken", async () => { await protocolDeploymentFixture(); - await WRBTC.mint(root, wei("500", "ether")); - await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); const receiver = accounts[1]; const restAmount = new BN("100"); // 100 wei - const previousBalanceReceiver = await WRBTC.balanceOf(receiver); - const feeSharingCollectorProxyBalance = await WRBTC.balanceOf( + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); const amount = feeSharingCollectorProxyBalance.sub(restAmount); - const previousBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const previousBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); expect(previousBalanceFeeSharingCollectorProxy.toString()).to.equal(wei("1", "ether")); - const tx = await feeSharingCollector.withdrawWRBTC(receiver, amount.toString()); + const tx = await feeSharingCollector.withdrawWrappedNativeToken( + receiver, + amount.toString() + ); await expectEvent.inTransaction( tx.receipt.rawLogs[0].transactionHash, - WRBTC, + WrappedNativeToken, "Transfer", { src: feeSharingCollector.address, @@ -5118,8 +5319,8 @@ contract("FeeSharingCollector:", (accounts) => { } ); - const latestBalanceReceiver = await WRBTC.balanceOf(receiver); - const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); @@ -5131,14 +5332,14 @@ contract("FeeSharingCollector:", (accounts) => { ); // try to withdraw the rest - const tx2 = await feeSharingCollector.withdrawWRBTC( + const tx2 = await feeSharingCollector.withdrawWrappedNativeToken( receiver, latestBalanceFeeSharingCollectorProxy.toString() ); - const finalBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf( + const finalBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); - const finalBalanceReceiver = await WRBTC.balanceOf(receiver); + const finalBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); expect(new BN(finalBalanceReceiver).toString()).to.equal( previousBalanceFeeSharingCollectorProxy.toString() ); @@ -5146,7 +5347,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectEvent.inTransaction( tx2.receipt.rawLogs[0].transactionHash, - WRBTC, + WrappedNativeToken, "Transfer", { src: feeSharingCollector.address, @@ -5157,8 +5358,8 @@ contract("FeeSharingCollector:", (accounts) => { }); }); - describe("get all rbtc balance after transferRBTC", async () => { - it("deposit 0 RBTC should revert", async () => { + describe("get all nativeToken balance after transferNativeToken", async () => { + it("deposit 0 NativeToken should revert", async () => { await protocolDeploymentFixture(); // stake - getPriorTotalVotingPower let totalStake = 1000; @@ -5167,18 +5368,20 @@ contract("FeeSharingCollector:", (accounts) => { let amount = 1000; await expectRevert( - feeSharingCollector.transferRBTC({ from: root, value: 0 }), - "FeeSharingCollector::transferRBTC: invalid value" + feeSharingCollector.transferNativeToken({ from: root, value: 0 }), + "FeeSharingCollector::transferNativeToken: invalid value" ); - const totalAccumulatedRBTCFee = - await feeSharingCollector.getAccumulatedRBTCFeeBalances(root); - expect(totalAccumulatedRBTCFee.toNumber()).to.equal(0); + const totalAccumulatedNativeTokenFee = + await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root); + expect(totalAccumulatedNativeTokenFee.toNumber()).to.equal(0); expect( - await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) + await feeSharingCollector.unprocessedAmount.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ) ).to.be.bignumber.equal(new BN(0)); }); - it("deposit RBTC should add the checkpoints", async () => { + it("deposit NativeToken should add the checkpoints", async () => { await protocolDeploymentFixture(); // stake - getPriorTotalVotingPower let totalStake = 1000; @@ -5186,13 +5389,15 @@ contract("FeeSharingCollector:", (accounts) => { let amount = 1000; - let tx = await feeSharingCollector.transferRBTC({ from: root, value: amount }); - let totalAccumulatedRBTCFee = - await feeSharingCollector.getAccumulatedRBTCFeeBalances(root); - expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount).toString()); + let tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount }); + let totalAccumulatedNativeTokenFee = + await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root); + expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString()); expect( - await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) + await feeSharingCollector.unprocessedAmount.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ) ).to.be.bignumber.equal(new BN(0)); expectEvent(tx, "TokensTransferred", { @@ -5203,11 +5408,11 @@ contract("FeeSharingCollector:", (accounts) => { // checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5218,27 +5423,29 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); expectEvent(tx, "CheckpointAdded", { sender: root, - token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + token: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, amount: new BN(amount), }); // second time - tx = await feeSharingCollector.transferRBTC({ from: root, value: amount * 2 }); - totalAccumulatedRBTCFee = - await feeSharingCollector.getAccumulatedRBTCFeeBalances(root); + tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount * 2 }); + totalAccumulatedNativeTokenFee = + await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root); // the deposit still in the window of withdraw interval, so the accumulatedFees won't be added at this point. - expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount).toString()); + expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString()); expect( - await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) + await feeSharingCollector.unprocessedAmount.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ) ).to.be.bignumber.equal(new BN(amount * 2)); expectEvent(tx, "TokensTransferred", { @@ -5249,25 +5456,29 @@ contract("FeeSharingCollector:", (accounts) => { await increaseTime(FEE_WITHDRAWAL_INTERVAL); // third time - tx = await feeSharingCollector.transferRBTC({ from: root, value: amount * 4 }); + tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount * 4 }); - totalAccumulatedRBTCFee = - await feeSharingCollector.getAccumulatedRBTCFeeBalances(root); + totalAccumulatedNativeTokenFee = + await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root); // already passed the withdrawal interval - expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount * 7).toString()); + expect(totalAccumulatedNativeTokenFee.toString()).to.equal( + new BN(amount * 7).toString() + ); expect( - await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) + await feeSharingCollector.unprocessedAmount.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ) ).to.be.bignumber.equal(new BN(0)); // checkpoints totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); checkpoint = await feeSharingCollector.tokenCheckpoints.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5278,7 +5489,7 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT ); block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -5287,7 +5498,7 @@ contract("FeeSharingCollector:", (accounts) => { describe("recover incorrect allocated fees", async () => { let mockSOV, mockZUSD; - let rbtcAmount = new BN(wei("878778886164898400", "wei")); + let nativeTokenAmount = new BN(wei("878778886164898400", "wei")); beforeEach(async () => { mockSOV = await smock.fake("TestToken", { @@ -5304,7 +5515,7 @@ contract("FeeSharingCollector:", (accounts) => { await web3.eth.sendTransaction({ from: accounts[2].toString(), to: feeSharingCollector.address, - value: rbtcAmount, + value: nativeTokenAmount, gas: 50000, }); }); @@ -5335,7 +5546,7 @@ contract("FeeSharingCollector:", (accounts) => { const latestBalanceOwner = new BN(await web3.eth.getBalance(owner)); const txFee = new BN((await etherGasCost(tx.receipt)).toString()); - expect(previousBalanceOwner.add(rbtcAmount).sub(txFee).toString()).to.be.equal( + expect(previousBalanceOwner.add(nativeTokenAmount).sub(txFee).toString()).to.be.equal( latestBalanceOwner.toString() ); }); @@ -5355,16 +5566,16 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Should revert if rbtc transfer failed", async () => { + it("Should revert if nativeToken transfer failed", async () => { feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - /** Should revert because feeSharingCollector does not have enough balance of rbtc */ + /** Should revert because feeSharingCollector does not have enough balance of nativeToken */ await expectRevert( feeSharingCollector.recoverIncorrectAllocatedFees(), - "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal rbtc failed" + "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal nativeToken failed" ); }); }); @@ -5381,7 +5592,7 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("getAccumulatedRBTCFeeBalances should revert loan wrbtc is not set", async () => { + it("getAccumulatedNativeTokenFeeBalances should revert loan wrappedNativeToken is not set", async () => { await protocolDeploymentFixture(); feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, @@ -5389,49 +5600,49 @@ contract("FeeSharingCollector:", (accounts) => { ); await expectRevert( - feeSharingCollector.getAccumulatedRBTCFeeBalances(root), + feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root), "Transaction reverted: function call to a non-contract account" ); }); - it("transferTokens (wrbtc) will revert if invalid total weighted stake", async () => { + it("transferTokens (wrappedNativeToken) will revert if invalid total weighted stake", async () => { await protocolDeploymentFixture(); feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - const mockWrbtc = await smock.fake("TestWrbtc"); - mockWrbtc.withdraw.returns(true); - await sovryn.setWrbtcToken(mockWrbtc.address); + const mockWrappedNativeToken = await smock.fake("TestWrappedNativeToken"); + mockWrappedNativeToken.withdraw.returns(true); + await sovryn.setWrbtcToken(mockWrappedNativeToken.address); - await WRBTC.mint(root, wei("500", "ether")); - await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether")); - await WRBTC.approve(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.approve(feeSharingCollector.address, wei("1", "ether")); await expectRevert( - feeSharingCollector.transferTokens(WRBTC.address, 1000), + feeSharingCollector.transferTokens(WrappedNativeToken.address, 1000), "Invalid totalWeightedStake" ); }); - it("transferTokens (wrbtc) wrbtc should success", async () => { + it("transferTokens (wrappedNativeToken) wrappedNativeToken should success", async () => { await protocolDeploymentFixture(); feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, staking.address ); - await WRBTC.mint(root, wei("500", "ether")); - await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether")); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); // stake - getPriorTotalVotingPower let totalStake = 1000; await stake(totalStake, root); let amount = 1000; - await WRBTC.approve(feeSharingCollector.address, amount * 7); + await WrappedNativeToken.approve(feeSharingCollector.address, amount * 7); - await feeSharingCollector.transferTokens(WRBTC.address, amount); + await feeSharingCollector.transferTokens(WrappedNativeToken.address, amount); }); it("endOfRange with 0 max checkpoint", async () => { @@ -5440,7 +5651,9 @@ contract("FeeSharingCollector:", (accounts) => { sovryn.address, staking.address ); - const end = await feeSharingCollector.endOfRangeWithZeroMaxCheckpoint(WRBTC.address); + const end = await feeSharingCollector.endOfRangeWithZeroMaxCheckpoint( + WrappedNativeToken.address + ); expect(end).to.equal(0); }); @@ -5464,7 +5677,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(hasFees).to.equal(false); }); - it("getRBTCBalance should revert error if non-rbtc token is passed", async () => { + it("getNativeTokenBalance should revert error if non-nativeToken token is passed", async () => { await protocolDeploymentFixture(); feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, @@ -5472,8 +5685,8 @@ contract("FeeSharingCollector:", (accounts) => { ); await expectRevert( - feeSharingCollector.getRBTCBalance(SOVToken.address, root, 0), - "FeeSharingCollector::_getRBTCBalance: only rbtc-based tokens are allowed" + feeSharingCollector.getNativeTokenBalance(SOVToken.address, root, 0), + "FeeSharingCollector::_getNativeTokenBalance: only nativeToken-based tokens are allowed" ); }); @@ -5516,13 +5729,13 @@ contract("FeeSharingCollector:", (accounts) => { lendingFee, tradingFee, borrowingFee, - wrbtcTokenFee = false, + wrappedNativeTokenFee = false, sovTokenFee = false ) { let totalFeeAmount = lendingFee.add(tradingFee).add(borrowingFee); let tokenFee; - if (wrbtcTokenFee) { - tokenFee = WRBTC; + if (wrappedNativeTokenFee) { + tokenFee = WrappedNativeToken; } else { tokenFee = SUSD; await tokenFee.transfer(sovryn.address, totalFeeAmount); @@ -5540,7 +5753,11 @@ contract("FeeSharingCollector:", (accounts) => { return totalFeeAmount; } - async function checkWithdrawFee(checkSUSD = true, checkWRBTC = false, checkSOV = false) { + async function checkWithdrawFee( + checkSUSD = true, + checkWrappedNativeToken = false, + checkSOV = false + ) { if (checkSUSD) { let protocolBalance = await SUSD.balanceOf(sovryn.address); expect(protocolBalance.toString()).to.be.equal(new BN(0).toString()); @@ -5552,12 +5769,18 @@ contract("FeeSharingCollector:", (accounts) => { expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); } - if (checkWRBTC) { - lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(WRBTC.address); + if (checkWrappedNativeToken) { + lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call( + WrappedNativeToken.address + ); expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); - tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(WRBTC.address); + tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call( + WrappedNativeToken.address + ); expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); - borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(WRBTC.address); + borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call( + WrappedNativeToken.address + ); expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); } diff --git a/tests/swaps/SwapsExternal.js b/tests/swaps/SwapsExternal.js index 51a1ef97d..007794ed3 100644 --- a/tests/swaps/SwapsExternal.js +++ b/tests/swaps/SwapsExternal.js @@ -487,7 +487,7 @@ contract("SwapsExternal", (accounts) => { // need to sub by swap fee because at this point, protocol will received the trading fee again. loanTokenWRBTCBalanceShouldBe = amount.mul(new BN(1)).sub(swapFee); - expectEvent(tx, "FeeWithdrawnInRBTC", { + expectEvent(tx, "FeeWithdrawnInNativeToken", { sender: lender, amount: loanTokenWRBTCBalanceShouldBe, }); From df9644a41b783581e13d01ee0c096ac136cd2034 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Fri, 24 May 2024 22:47:53 +0700 Subject: [PATCH 02/15] add withdrawFeesFromDex function --- .../FeeSharingCollector.sol | 96 +++++++++++ .../FeeSharingCollectorStorage.sol | 5 + contracts/interfaces/ISovrynDex.sol | 5 + contracts/mockup/MockSovrynDex.sol | 39 +++++ tests/FeeSharingCollectorTest.js | 158 ++++++++++++++++++ 5 files changed, 303 insertions(+) create mode 100644 contracts/interfaces/ISovrynDex.sol create mode 100644 contracts/mockup/MockSovrynDex.sol diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index bbd9438e9..6ae8a58cd 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -9,6 +9,7 @@ import "../IFeeSharingCollector.sol"; import "../../openzeppelin/Address.sol"; import "./FeeSharingCollectorStorage.sol"; import "../../interfaces/IConverterAMM.sol"; +import "../../interfaces/ISovrynDex.sol"; /** * @title The FeeSharingCollector contract. @@ -58,6 +59,8 @@ contract FeeSharingCollector is address constant ZERO_ADDRESS = address(0); address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); + uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; + uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; /* Events */ @@ -96,6 +99,15 @@ contract FeeSharingCollector is */ event FeeAMMWithdrawn(address indexed sender, address indexed converter, uint256 amount); + /** + * @notice An event emitted when fee from Dex get withdrawn. + * + * @param sender sender who initiate the withdrawn dex protocol fees. + * @param token the token address. + * @param amount total amount of fee (Already converted to wrappedNativeToken). + */ + event FeeDexWithdrawn(address indexed sender, address indexed token, uint256 amount); + /// @notice An event emitted when converter address has been registered to be whitelisted. event WhitelistedConverter(address indexed sender, address converter); @@ -118,6 +130,12 @@ contract FeeSharingCollector is event SetProtocolAddress(address indexed sender, address _protocolAddress); + event SetSovrynDexAddress( + address indexed sender, + address indexed oldSovrynDexAddress, + address indexed newSovrynDexAddress + ); + /* Modifier */ modifier oneTimeExecution(bytes4 _funcSig) { require( @@ -186,6 +204,14 @@ contract FeeSharingCollector is emit SetProtocolAddress(msg.sender, _protocolAddress); } + function setSovrynDexAddress( + address _sovrynDexAddress + ) public onlyOwner oneTimeExecution(this.setSovrynDexAddress.selector) { + require(Address.isContract(_sovrynDexAddress), "_sovrynDexAddress not a contract"); + emit SetSovrynDexAddress(msg.sender, sovrynDexAddress, _sovrynDexAddress); + sovrynDexAddress = _sovrynDexAddress; + } + /** * @notice Set the loan wrappedNativeToken token address of fee sharing collector. * @@ -298,6 +324,63 @@ contract FeeSharingCollector is } } + /** + * @notice Withdraw amm fees for the given converter addresses: + * protocolFee from the conversion + * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool + * + * @param _tokens array addresses of the tokens + * */ + function withdrawFeesFromDex(address[] memory _tokens) public { + for (uint256 i = 0; i < _tokens.length; i++) { + require( + Address.isContract(_tokens[i]), + "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" + ); + } + + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); + + uint96 totalPoolTokenAmount; + for (uint256 i = 0; i < _tokens.length; i++) { + address token = _tokens[i]; + /** Withdraw from dex */ + bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, token); + bytes memory withdrawnData = ISovrynDex(sovrynDexAddress).userCmd( + SOVRYN_DEX_COLD_PATH_PROXY_IDX, + cmd + ); + + // Convert the return bytes data from dex to uint256 + uint256 wrappedNativeTokenAmountWithdrawn = bytesToUint256(withdrawnData); + + if (wrappedNativeTokenAmountWithdrawn > 0) { + // unwrap wrappedNativeToken to nativeToken, and hold the nativeToken + wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn); + + /// @notice Update unprocessed amount of tokens + uint96 amount96 = safe96( + wrappedNativeTokenAmountWithdrawn, + "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken token amount exceeds 96 bits" + ); + + totalPoolTokenAmount = add96( + totalPoolTokenAmount, + amount96, + "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken token amount exceeds 96 bits" + ); + + emit FeeDexWithdrawn(msg.sender, token, wrappedNativeTokenAmountWithdrawn); + } + } + + if (totalPoolTokenAmount > 0) { + _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); + } + } + /** * @notice Transfer tokens to this contract. * @dev We just update amount of tokens here and write checkpoint in a separate methods @@ -1295,6 +1378,19 @@ contract FeeSharingCollector is function numTokenCheckpoints(address _token) external view returns (uint256) { return totalTokenCheckpoints[_token]; } + + // Function to convert bytes to uint256 + function bytesToUint256(bytes memory b) private pure returns (uint256) { + require(b.length == 32, "Invalid bytes length, must be 32 bytes"); + + uint256 result; + + assembly { + result := mload(add(b, 0x20)) + } + + return result; + } } interface ILoanWrappedNativeToken { diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol index 790f8c810..68f6ef8ab 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol @@ -86,6 +86,11 @@ contract FeeSharingCollectorStorage is Ownable { */ address public loanWrappedNativeTokenAddress; + /** + * @dev sovrynDex + */ + address public sovrynDexAddress; + /** * @dev Prevents a contract from calling itself, directly or indirectly. * If you mark a function `nonReentrant`, you should also diff --git a/contracts/interfaces/ISovrynDex.sol b/contracts/interfaces/ISovrynDex.sol new file mode 100644 index 000000000..6b08beede --- /dev/null +++ b/contracts/interfaces/ISovrynDex.sol @@ -0,0 +1,5 @@ +pragma solidity 0.5.17; + +interface ISovrynDex { + function userCmd(uint16 callpath, bytes calldata cmd) external payable returns (bytes memory); +} diff --git a/contracts/mockup/MockSovrynDex.sol b/contracts/mockup/MockSovrynDex.sol new file mode 100644 index 000000000..36419233d --- /dev/null +++ b/contracts/mockup/MockSovrynDex.sol @@ -0,0 +1,39 @@ +pragma solidity 0.5.17; + +import "../interfaces/IERC20.sol"; + +contract MockSovrynDex { + mapping(address => uint256) public tokenFees; + uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; + uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; + IERC20 wrbtcToken; + address treasury; + + constructor() public {} + + function setTreasury(address _treasury) public { + treasury = _treasury; + } + + function setWrbtcToken(IERC20 _wrbtcToken) public { + wrbtcToken = _wrbtcToken; + } + + function userCmd(uint16 callpath, bytes calldata cmd) external payable returns (bytes memory) { + require(msg.sender == treasury, "Only Treasury"); + (uint8 cmdCode, address token) = abi.decode(cmd, (uint8, address)); + if ( + callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && + cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE + ) { + wrbtcToken.transfer(msg.sender, tokenFees[token]); + return abi.encode(tokenFees[token]); + } else { + return "0x"; + } + } + + function setTokenDexFee(address token, uint256 fee) external { + tokenFees[token] = fee; + } +} diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index a2146784d..f873b55ab 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -80,6 +80,8 @@ const SwapsExternal = artifacts.require("SwapsExternal"); const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup"); const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup"); +const MockSovrynDex = artifacts.require("MockSovrynDex"); + const TOTAL_SUPPLY = etherMantissa(1000000000); const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092)); @@ -134,6 +136,7 @@ contract("FeeSharingCollector:", (accounts) => { let mockPrice; let liquidityPoolV1Converter; let iWeightedStakingModuleMockup; + let sovrynDex; before(async () => { [root, account1, account2, account3, account4, ...accounts] = accounts; @@ -328,6 +331,9 @@ contract("FeeSharingCollector:", (accounts) => { loanWrappedNativeToken.address ); + sovrynDex = await MockSovrynDex.new(); + await feeSharingCollector.setSovrynDexAddress(sovrynDex.address); + return sovryn; } @@ -494,6 +500,13 @@ contract("FeeSharingCollector:", (accounts) => { newLoanWrappedNativeTokenAddress ); }); + + it("setSovrynDexAddress should set the sovrynDexAddress properly", async () => { + expect(await feeSharingCollector.sovrynDexAddress()).to.equal(ZERO_ADDRESS); + const newSovrynDexAddress = (await MockSovrynDex.new()).address; + await feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress); + expect(await feeSharingCollector.sovrynDexAddress()).to.equal(newSovrynDexAddress); + }); }); describe("withdrawStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => { @@ -5190,6 +5203,151 @@ contract("FeeSharingCollector:", (accounts) => { }); }); + describe("withdraw Dex Fees", async () => { + it("should not be able to withdraw fees if invalid token address", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([accounts[0]]), + "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" + ); + }); + + it("Should be able to withdraw Dex Fees", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([SUSD.address]), + "Only Treasury" + ); + + //mock data + const feeAmount = new BN(wei("1", "ether")); + await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); + await sovrynDex.setTreasury(feeSharingCollector.address); + await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + + await WrappedNativeToken.mint(sovrynDex.address, wei("2", "ether")); + + let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( + await web3.eth.getBalance(feeSharingCollector.address) + ); + tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); + + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); + + // nativeToken balance of feeSharingCollector should be increased + let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( + await web3.eth.getBalance(feeSharingCollector.address) + ); + expect( + previousFeeSharingCollectorProxyNativeTokenBalance + .add(new BN(feeAmount)) + .toString() + ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( + new BN(0).toString() + ); + + //checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + 0 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); + + //check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + expectEvent(tx, "FeeDexWithdrawn", { + sender: root, + token: SUSD.address, + amount: feeAmount, + }); + }); + + it("Should be able to withdraw with 0 AMM Fees", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([SUSD.address]), + "Only Treasury" + ); + + //mock data + const feeAmount = new BN(wei("0", "ether")); + await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); + await sovrynDex.setTreasury(feeSharingCollector.address); + await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + + await WrappedNativeToken.mint(sovrynDex.address, wei("2", "ether")); + + tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); + //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingCollectorProxyWrappedNativeTokenBalance = + await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); + expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( + new BN(0).toString() + ); + + //checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + loanWrappedNativeToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(0); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call( + loanWrappedNativeToken.address, + 0 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(0); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(0); + expect(checkpoint.numTokens.toString()).to.be.equal("0"); + + //check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + loanWrappedNativeToken.address + ); + expect(lastFeeWithdrawalTime.toString()).to.be.equal("0"); + }); + }); + describe("withdraw wrappedNativeToken", async () => { it("Withdraw wrappedNativeToken from non owner should revert", async () => { await protocolDeploymentFixture(); From a15d0ad7d0417cda93a6545e11a1b57384fdee81 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Thu, 6 Jun 2024 09:08:35 +0700 Subject: [PATCH 03/15] fix: unit test --- tests/FeeSharingCollectorTest.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index f873b55ab..fd1b1593c 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -501,11 +501,9 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("setSovrynDexAddress should set the sovrynDexAddress properly", async () => { - expect(await feeSharingCollector.sovrynDexAddress()).to.equal(ZERO_ADDRESS); + it("setSovrynDexAddress should only be called once", async () => { const newSovrynDexAddress = (await MockSovrynDex.new()).address; - await feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress); - expect(await feeSharingCollector.sovrynDexAddress()).to.equal(newSovrynDexAddress); + await expectRevert(feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), "FeeSharingCollector: function can only be called once"); }); }); From cd3cfe721d63faeb06c079218e51e5e926659262 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Thu, 6 Jun 2024 09:10:08 +0700 Subject: [PATCH 04/15] add check in unit test --- tests/FeeSharingCollectorTest.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index fd1b1593c..18fdd8d60 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -502,8 +502,12 @@ contract("FeeSharingCollector:", (accounts) => { }); it("setSovrynDexAddress should only be called once", async () => { + expect(await feeSharingCollector.sovrynDexAddress()).to.equal(sovrynDex.address); const newSovrynDexAddress = (await MockSovrynDex.new()).address; - await expectRevert(feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), "FeeSharingCollector: function can only be called once"); + await expectRevert( + feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), + "FeeSharingCollector: function can only be called once" + ); }); }); From b931a512e51331ad5b8bfca709d367ae9493a36d Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Thu, 13 Jun 2024 22:24:56 +0700 Subject: [PATCH 05/15] init: feeSharingCollectorMultipleToken --- .../FeeSharingCollector.sol | 10 - .../FeeSharingCollectorMultipleToken.sol | 622 ++++ .../FeeSharingCollectorStorage.sol | 11 + .../IFeeSharingCollectorMultipleToken.sol | 19 + ...FeeSharingCollectorMultipleTokenMockup.sol | 42 + .../mockup/MockSovrynDexMultipleToken.sol | 37 + .../FeeSharingCollectorMultipleToken.test.js | 2947 +++++++++++++++++ 7 files changed, 3678 insertions(+), 10 deletions(-) create mode 100644 contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol create mode 100644 contracts/governance/IFeeSharingCollectorMultipleToken.sol create mode 100644 contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol create mode 100644 contracts/mockup/MockSovrynDexMultipleToken.sol create mode 100644 tests/FeeSharingCollectorMultipleToken.test.js diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index 6ae8a58cd..4cd8bda2f 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -136,16 +136,6 @@ contract FeeSharingCollector is address indexed newSovrynDexAddress ); - /* Modifier */ - modifier oneTimeExecution(bytes4 _funcSig) { - require( - !isFunctionExecuted[_funcSig], - "FeeSharingCollector: function can only be called once" - ); - _; - isFunctionExecuted[_funcSig] = true; - } - /* Functions */ /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken. diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol new file mode 100644 index 000000000..8c789df3e --- /dev/null +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol @@ -0,0 +1,622 @@ +pragma solidity ^0.5.17; +pragma experimental ABIEncoderV2; + +import "../Staking/SafeMath96.sol"; +import "../../openzeppelin/SafeMath.sol"; +import "../../openzeppelin/SafeERC20.sol"; +import "../../openzeppelin/Ownable.sol"; +import "../IFeeSharingCollectorMultipleToken.sol"; +import "../../openzeppelin/Address.sol"; +import "./FeeSharingCollectorStorage.sol"; +import "../../interfaces/ISovrynDex.sol"; + +/** + * @title The FeeSharingCollectorMultipleToken contract. + * @notice Staking is not only granting voting rights, but also access to fee + * sharing according to the own voting power in relation to the total. Whenever + * somebody decides to collect the fees from the protocol, they get transferred + * to a proxy contract which invests the funds in the lending pool and keeps + * the pool tokens. + * + * The fee sharing proxy will be set as feesController of the protocol contract. + * This allows the fee sharing proxy to withdraw the fees. The fee sharing + * proxy holds the pool tokens and keeps track of which user owns how many + * tokens. In order to know how many tokens a user owns, the fee sharing proxy + * needs to know the user’s weighted stake in relation to the total weighted + * stake (aka total voting power). + * + * Because both values are subject to change, they may be different on each fee + * withdrawal. To be able to calculate a user’s share of tokens when he wants + * to withdraw, we need checkpoints. + * + * This contract is intended to be set as the protocol fee collector. + * Anybody can invoke the withdrawFees function which uses + * protocol.withdrawFees to obtain available fees from operations on a + * certain token. These fees are deposited in the corresponding loanPool. + * Also, the staking contract sends slashed tokens to this contract. When a + * user calls the withdraw function, the contract transfers the fee sharing + * rewards in proportion to the user’s weighted stake since the last withdrawal. + * + * The protocol is collecting fees in all sorts of currencies and then automatically + * supplies them to the respective lending pools. Therefore, all fees are + * generating interest for the SOV holders. If one of them withdraws fees, it will + * get pool tokens. It is planned to add the option to convert anything to rBTC + * before withdrawing, but not yet implemented. + * */ +contract FeeSharingCollectorMultipleToken is + SafeMath96, + IFeeSharingCollectorMultipleToken, + Ownable, + FeeSharingCollectorStorage +{ + using SafeMath for uint256; + using SafeERC20 for IERC20; + + address constant ZERO_ADDRESS = address(0); + address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = + address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); + uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; + uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; + + /* Events */ + + /// @notice An event emitted when fee get withdrawn. + event FeeWithdrawn(address indexed sender, address indexed token, uint256 amount); + + /// @notice An event emitted when tokens transferred. + event TokensTransferred(address indexed sender, address indexed token, uint256 amount); + + /// @notice An event emitted when checkpoint added. + event CheckpointAdded(address indexed sender, address indexed token, uint256 amount); + + /// @notice An event emitted when user fee get withdrawn. + event UserFeeWithdrawn( + address indexed sender, + address indexed receiver, + address indexed token, + uint256 amount + ); + + event SetWrappedNativeToken( + address indexed sender, + address indexed oldWrappedNativeToken, + address indexed newWrappedNativeToken + ); + + event SetProtocolAddress(address indexed sender, address _protocolAddress); + + event SetSovrynDexAddress( + address indexed sender, + address indexed oldSovrynDexAddress, + address indexed newSovrynDexAddress + ); + + /* Functions */ + /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken. + function() external payable {} + + /** + * @dev initialize function for fee sharing collector proxy + * @param wrappedNativeToken wrappedNativeToken token address + * @param dexAddress wrappedNativeToken token address + */ + function initialize( + address wrappedNativeToken, + address dexAddress + ) external onlyOwner oneTimeExecution(this.initialize.selector) { + setWrappedNativeToken(wrappedNativeToken); + setSovrynDexAddress(dexAddress); + } + + /** + * @notice Set the wrappedNativeToken token address of fee sharing collector. + * + * only owner can perform this action. + * + * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken token. + * */ + function setWrappedNativeToken( + address newWrappedNativeTokenAddress + ) public onlyOwner oneTimeExecution(this.setWrappedNativeToken.selector) { + require( + Address.isContract(newWrappedNativeTokenAddress), + "newWrappedNativeTokenAddress not a contract" + ); + emit SetWrappedNativeToken( + msg.sender, + wrappedNativeTokenAddress, + newWrappedNativeTokenAddress + ); + wrappedNativeTokenAddress = newWrappedNativeTokenAddress; + } + + /** + * @notice Set the Protocol address if not set at initial deployment of the proxy contract + * + * only owner can perform this action. + * + * @param _protocolAddress Sovryn protocol address. + * */ + function setProtocolAddress( + address _protocolAddress + ) public onlyOwner oneTimeExecution(this.setProtocolAddress.selector) { + require(Address.isContract(_protocolAddress), "_protocolAddress not a contract"); + require(address(protocol) == address(0x0), "protocol address already set"); + protocol = IProtocol(_protocolAddress); + emit SetProtocolAddress(msg.sender, _protocolAddress); + } + + function setSovrynDexAddress( + address _sovrynDexAddress + ) public onlyOwner oneTimeExecution(this.setSovrynDexAddress.selector) { + require(Address.isContract(_sovrynDexAddress), "_sovrynDexAddress not a contract"); + emit SetSovrynDexAddress(msg.sender, sovrynDexAddress, _sovrynDexAddress); + sovrynDexAddress = _sovrynDexAddress; + } + + /** + * @notice Withdraw fees for the given token: + * lendingFee + tradingFee + borrowingFee + * the fees (except SOV) will be converted in wRBTC form, and then will be transferred to wRBTC loan pool. + * For SOV, it will be directly deposited into the FeeSharingCollectorMultipleToken from the protocol. + * + * @param _tokens array address of the token + * */ + function withdrawFees(address[] memory _tokens) public { + for (uint256 i = 0; i < _tokens.length; i++) { + require( + Address.isContract(_tokens[i]), + "FeeSharingCollectorMultipleToken::withdrawFees: token is not a contract" + ); + } + + uint256 wrbtcAmountWithdrawn = protocol.withdrawFees(_tokens, address(this)); + uint256 poolTokenAmount; + + address wRBTCAddress = wrappedNativeTokenAddress; + require( + wRBTCAddress != address(0), + "FeeSharingCollectorMultipleToken::withdrawFees: wRBTCAddress is not set" + ); + + address loanPoolToken = protocol.underlyingToLoanPool(wRBTCAddress); + require( + loanPoolToken != address(0), + "FeeSharingCollectorMultipleToken::withdrawFees: loan wRBTC not found" + ); + + if (wrbtcAmountWithdrawn > 0) { + /// @dev TODO can be also used - function addLiquidity(IERC20Token _reserveToken, uint256 _amount, uint256 _minReturn) + IERC20(wRBTCAddress).approve(loanPoolToken, wrbtcAmountWithdrawn); + poolTokenAmount = ILoanToken(loanPoolToken).mint(address(this), wrbtcAmountWithdrawn); + + /// @notice Update unprocessed amount of tokens + uint96 amount96 = safe96( + poolTokenAmount, + "FeeSharingCollectorMultipleToken::withdrawFees: pool token amount exceeds 96 bits" + ); + + _addCheckpoint(loanPoolToken, amount96); + } + + emit FeeWithdrawn(msg.sender, loanPoolToken, poolTokenAmount); + } + + /** + * @notice Withdraw fees from sovryn dex: + * protocolFee from the conversion + * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool + * + * @param _tokens array addresses of the tokens + * */ + function withdrawFeesFromDex(address[] memory _tokens) public { + for (uint256 i = 0; i < _tokens.length; i++) { + require( + Address.isContract(_tokens[i]), + "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" + ); + } + + for (uint256 i = 0; i < _tokens.length; i++) { + address token = _tokens[i]; + /** Withdraw from dex */ + bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, token); + ISovrynDex(sovrynDexAddress).userCmd(SOVRYN_DEX_COLD_PATH_PROXY_IDX, cmd); + } + } + + /** + * @notice Transfer tokens to this contract. + * @dev We just update amount of tokens here and write checkpoint in a separate methods + * in order to prevent adding checkpoints too often. + * @param _token Address of the token. + * @param _amount Amount to be transferred. + * */ + function transferTokens(address _token, uint96 _amount) public { + require( + _token != ZERO_ADDRESS, + "FeeSharingCollectorMultipleToken::transferTokens: invalid address" + ); + require(_amount > 0, "FeeSharingCollectorMultipleToken::transferTokens: invalid amount"); + + /// @notice Transfer tokens from msg.sender + bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount); + require(success, "Staking::transferTokens: token transfer failed"); + + // if _token is wrappedNativeToken, need to unwrap it to nativeToken + IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( + wrappedNativeTokenAddress + ); + if (_token == address(wrappedNativeToken)) { + wrappedNativeToken.withdraw(_amount); + _token = NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; + } + + _addCheckpoint(_token, _amount); + + emit TokensTransferred(msg.sender, _token, _amount); + } + + /** + * @notice Add checkpoint with accumulated amount by function invocation. + * @param _token Address of the token. + * */ + function _addCheckpoint(address _token, uint96 _amount) internal { + if (block.timestamp - lastFeeWithdrawalTime[_token] >= FEE_WITHDRAWAL_INTERVAL) { + lastFeeWithdrawalTime[_token] = block.timestamp; + uint96 amount = add96( + unprocessedAmount[_token], + _amount, + "FeeSharingCollectorMultipleToken::_addCheckpoint: amount exceeds 96 bits" + ); + + /// @notice Reset unprocessed amount of tokens to zero. + unprocessedAmount[_token] = 0; + + /// @notice Write a regular checkpoint. + _writeTokenCheckpoint(_token, amount); + } else { + unprocessedAmount[_token] = add96( + unprocessedAmount[_token], + _amount, + "FeeSharingCollectorMultipleToken::_addCheckpoint: unprocessedAmount exceeds 96 bits" + ); + } + } + + /** + * @notice Withdraw accumulated fee to the message sender. + * + * The Sovryn protocol collects fees on every trade/swap and loan. + * These fees will be distributed to SOV stakers based on their voting + * power as a percentage of total voting power. Therefore, staking more + * SOV and/or staking for longer will increase your share of the fees + * generated, meaning you will earn more from staking. + * + * This function will directly burnToBTC and use the msg.sender (user) as the receiver + * + * @param _token Addresses of the pool token. + * @param _maxCheckpoint Maximum number of checkpoints to be processed. + * @param _receiver The receiver of tokens or msg.sender + * */ + function withdraw( + address _token, + uint32 _maxCheckpoint, + address _receiver + ) public nonReentrant { + _withdraw(_token, _maxCheckpoint, _receiver); + } + + /** + * @notice Withdraw accumulated fees (multiple token) to the message sender. + * + * The Sovryn protocol collects fees on every trade/swap and loan. + * These fees will be distributed to SOV stakers based on their voting + * power as a percentage of total voting power. Therefore, staking more + * SOV and/or staking for longer will increase your share of the fees + * generated, meaning you will earn more from staking. + * + * This function will directly burnToBTC and use the msg.sender (user) as the receiver + * + * @param _tokens Array of Addresses of the pool token. + * @param _maxCheckpoints Array of Maximum number of checkpoints to be processed. + * @param _receiver The receiver of tokens or msg.sender + * */ + function withdrawTokens( + address[] memory _tokens, + uint32[] memory _maxCheckpoints, + address _receiver + ) public nonReentrant { + require( + _tokens.length == _maxCheckpoints.length, + "length mismatch _tokens <> _maxCheckpoints" + ); + + for (uint256 i = 0; i < _tokens.length; i++) { + address _token = _tokens[i]; + uint32 _maxCheckpoint = _maxCheckpoints[i]; + + _withdraw(_token, _maxCheckpoint, _receiver); + } + } + + /** + * @notice Internal function to withdraw accumulated fee to the message sender. + * + * The Sovryn protocol collects fees on every trade/swap and loan. + * These fees will be distributed to SOV stakers based on their voting + * power as a percentage of total voting power. Therefore, staking more + * SOV and/or staking for longer will increase your share of the fees + * generated, meaning you will earn more from staking. + * + * This function will directly burnToBTC and use the msg.sender (user) as the receiver + * + * @param _token Address of the pool token. + * @param _maxCheckpoint Maximum number of checkpoints to be processed. + * @param _receiver The receiver of tokens or msg.sender + * */ + function _withdraw(address _token, uint32 _maxCheckpoint, address _receiver) internal { + /// @dev Prevents processing / checkpoints because of block gas limit. + require( + _maxCheckpoint > 0, + "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + ); + + address _wrappedNativeTokenAddress = wrappedNativeTokenAddress; + require( + _wrappedNativeTokenAddress != address(0), + "FeeSharingCollectorMultipleToken::withdraw: _wrappedNativeTokenAddress is not set" + ); + + address loanWrappedNativeToken = protocol.underlyingToLoanPool(_wrappedNativeTokenAddress); + require( + loanWrappedNativeToken != address(0), + "FeeSharingCollectorMultipleToken::withdraw: loan wrapped native token not found" + ); + + address user = msg.sender; + if (_receiver == address(0)) { + _receiver = msg.sender; + } + + uint256 amount; + uint256 end; + (amount, end) = _getAccumulatedFees(user, _token, _maxCheckpoint); + require( + amount > 0, + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + + processedCheckpoints[user][_token] = end; + + if (loanWrappedNativeToken == _token) { + // We will change, so that FeeSharingCollectorMultipleToken will directly burn then loanToken (IWRBTC) to rbtc and send to the user --- by call burnToBTC function + uint256 loanAmountPaid = ILoanWrappedNativeToken(_token).burnToBTC( + _receiver, + amount, + false + ); + } else { + // Previously it directly send the token to the user + require( + IERC20(_token).transfer(_receiver, amount), + "FeeSharingCollectorMultipleToken::withdraw: withdrawal failed" + ); + } + + emit UserFeeWithdrawn(msg.sender, _receiver, _token, amount); + } + + /** + * @notice Get the accumulated loan pool fee of the message sender. + * @param _user The address of the user or contract. + * @param _loanPoolToken Address of the pool token. + * @return The accumulated fee for the message sender. + * */ + function getAccumulatedFees( + address _user, + address _loanPoolToken + ) public view returns (uint256) { + uint256 amount; + (amount, ) = _getAccumulatedFees(_user, _loanPoolToken, 0); + return amount; + } + + /** + * @notice Whenever fees are withdrawn, the staking contract needs to + * checkpoint the block number, the number of pool tokens and the + * total voting power at that time (read from the staking contract). + * While the total voting power would not necessarily need to be + * checkpointed, it makes sense to save gas cost on withdrawal. + * + * When the user wants to withdraw its share of tokens, we need + * to iterate over all of the checkpoints since the users last + * withdrawal (note: remember last withdrawal block), query the + * user’s balance at the checkpoint blocks from the staking contract, + * compute his share of the checkpointed tokens and add them up. + * The maximum number of checkpoints to process at once should be limited. + * + * @param _user Address of the user's account. + * @param _loanPoolToken Loan pool token address. + * @param _maxCheckpoints Checkpoint index incremental. + * */ + function _getAccumulatedFees( + address _user, + address _loanPoolToken, + uint32 _maxCheckpoints + ) internal view returns (uint256, uint256) { + if (staking.isVestingContract(_user)) { + return (0, 0); + } + + uint256 start = processedCheckpoints[_user][_loanPoolToken]; + uint256 end; + + /// @dev Additional bool param can't be used because of stack too deep error. + if (_maxCheckpoints > 0) { + /// @dev withdraw -> _getAccumulatedFees + require( + start < totalTokenCheckpoints[_loanPoolToken], + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + end = _getEndOfRange(start, _loanPoolToken, _maxCheckpoints); + } else { + /// @dev getAccumulatedFees -> _getAccumulatedFees + /// Don't throw error for getter invocation outside of transaction. + if (start >= totalTokenCheckpoints[_loanPoolToken]) { + return (0, totalTokenCheckpoints[_loanPoolToken]); + } + end = totalTokenCheckpoints[_loanPoolToken]; + } + + uint256 amount = 0; + uint256 cachedLockDate = 0; + uint96 cachedWeightedStake = 0; + for (uint256 i = start; i < end; i++) { + Checkpoint storage checkpoint = tokenCheckpoints[_loanPoolToken][i]; + uint256 lockDate = staking.timestampToLockDate(checkpoint.timestamp); + uint96 weightedStake; + if (lockDate == cachedLockDate) { + weightedStake = cachedWeightedStake; + } else { + /// @dev We need to use "checkpoint.blockNumber - 1" here to calculate weighted stake + /// For the same block like we did for total voting power in _writeTokenCheckpoint + weightedStake = staking.getPriorWeightedStake( + _user, + checkpoint.blockNumber - 1, + checkpoint.timestamp + ); + cachedWeightedStake = weightedStake; + cachedLockDate = lockDate; + } + uint256 share = uint256(checkpoint.numTokens).mul(weightedStake).div( + uint256(checkpoint.totalWeightedStake) + ); + amount = amount.add(share); + } + return (amount, end); + } + + /** + * @notice Withdrawal should only be possible for blocks which were already + * mined. If the fees are withdrawn in the same block as the user withdrawal + * they are not considered by the withdrawing logic (to avoid inconsistencies). + * + * @param start Start of the range. + * @param _loanPoolToken Loan pool token address. + * @param _maxCheckpoints Checkpoint index incremental. + * */ + function _getEndOfRange( + uint256 start, + address _loanPoolToken, + uint32 _maxCheckpoints + ) internal view returns (uint256) { + uint256 nCheckpoints = totalTokenCheckpoints[_loanPoolToken]; + uint256 end; + if (_maxCheckpoints == 0) { + /// @dev All checkpoints will be processed (only for getter outside of a transaction). + end = nCheckpoints; + } else { + if (_maxCheckpoints > MAX_CHECKPOINTS) { + _maxCheckpoints = MAX_CHECKPOINTS; + } + end = safe32( + start + _maxCheckpoints, + "FeeSharingCollectorMultipleToken::withdraw: checkpoint index exceeds 32 bits" + ); + if (end > nCheckpoints) { + end = nCheckpoints; + } + } + + /// @dev Withdrawal should only be possible for blocks which were already mined. + uint32 lastBlockNumber = tokenCheckpoints[_loanPoolToken][end - 1].blockNumber; + if (block.number == lastBlockNumber) { + end--; + } + return end; + } + + /** + * @notice Write a regular checkpoint w/ the foolowing data: + * block number, block timestamp, total weighted stake and num of tokens. + * @param _token The pool token address. + * @param _numTokens The amount of pool tokens. + * */ + function _writeTokenCheckpoint(address _token, uint96 _numTokens) internal { + uint32 blockNumber = safe32( + block.number, + "FeeSharingCollectorMultipleToken::_writeCheckpoint: block number exceeds 32 bits" + ); + uint32 blockTimestamp = safe32( + block.timestamp, + "FeeSharingCollectorMultipleToken::_writeCheckpoint: block timestamp exceeds 32 bits" + ); + uint256 nCheckpoints = totalTokenCheckpoints[_token]; + + uint96 totalWeightedStake = _getVoluntaryWeightedStake(blockNumber - 1, block.timestamp); + require(totalWeightedStake > 0, "Invalid totalWeightedStake"); + if ( + nCheckpoints > 0 && + tokenCheckpoints[_token][nCheckpoints - 1].blockNumber == blockNumber + ) { + tokenCheckpoints[_token][nCheckpoints - 1].totalWeightedStake = totalWeightedStake; + tokenCheckpoints[_token][nCheckpoints - 1].numTokens = _numTokens; + } else { + tokenCheckpoints[_token][nCheckpoints] = Checkpoint( + blockNumber, + blockTimestamp, + totalWeightedStake, + _numTokens + ); + totalTokenCheckpoints[_token] = nCheckpoints + 1; + } + emit CheckpointAdded(msg.sender, _token, _numTokens); + } + + /** + * Queries the total weighted stake and the weighted stake of vesting contracts and returns the difference + * @param blockNumber the blocknumber + * @param timestamp the timestamp + */ + function _getVoluntaryWeightedStake( + uint32 blockNumber, + uint256 timestamp + ) internal view returns (uint96 totalWeightedStake) { + uint96 vestingWeightedStake = staking.getPriorVestingWeightedStake(blockNumber, timestamp); + totalWeightedStake = staking.getPriorTotalVotingPower(blockNumber, timestamp); + totalWeightedStake = sub96( + totalWeightedStake, + vestingWeightedStake, + "FeeSharingCollectorMultipleToken::_getTotalVoluntaryWeightedStake: vested stake exceeds total stake" + ); + } + + function withdrawWRBTC(address receiver, uint256 wrbtcAmount) external onlyOwner { + address wRBTCAddress = wrappedNativeTokenAddress; + require( + wRBTCAddress != address(0), + "FeeSharingCollectorMultipleToken::withdrawFees: wRBTCAddress is not set" + ); + + uint256 balance = IERC20(wRBTCAddress).balanceOf(address(this)); + require(wrbtcAmount <= balance, "Insufficient balance"); + + IERC20(wRBTCAddress).safeTransfer(receiver, wrbtcAmount); + } +} + +/* Interfaces */ +interface ILoanToken { + function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount); +} + +interface ILoanWrappedNativeToken { + function burnToBTC( + address receiver, + uint256 burnAmount, + bool useLM + ) external returns (uint256 loanAmountPaid); + + function tokenPrice() external view returns (uint256 price); +} diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol index 68f6ef8ab..dcd6f726b 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol @@ -16,6 +16,7 @@ import "../../interfaces/IWrappedNativeTokenERC20.sol"; contract FeeSharingCollectorStorage is Ownable { using EnumerableAddressSet for EnumerableAddressSet.AddressSet; uint256 constant FEE_WITHDRAWAL_INTERVAL = 172800; + uint32 constant MAX_CHECKPOINTS = 100; IProtocol public protocol; IStaking public staking; @@ -105,6 +106,16 @@ contract FeeSharingCollectorStorage is Ownable { _; reentrancyLock = REENTRANCY_GUARD_FREE; } + + /* Modifier */ + modifier oneTimeExecution(bytes4 _funcSig) { + require( + !isFunctionExecuted[_funcSig], + "FeeSharingCollector: function can only be called once" + ); + _; + isFunctionExecuted[_funcSig] = true; + } } /* Interfaces */ diff --git a/contracts/governance/IFeeSharingCollectorMultipleToken.sol b/contracts/governance/IFeeSharingCollectorMultipleToken.sol new file mode 100644 index 000000000..473c2ffeb --- /dev/null +++ b/contracts/governance/IFeeSharingCollectorMultipleToken.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.5.17; + +/** + * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol + * @dev Interfaces are used to cast a contract address into a callable instance. + * */ +interface IFeeSharingCollectorMultipleToken { + function withdrawFees(address[] calldata _token) external; + + function transferTokens(address _token, uint96 _amount) external; + + function withdraw(address _token, uint32 _maxCheckpoint, address _receiver) external; + + function withdrawTokens( + address[] calldata _tokens, + uint32[] calldata _maxCheckpoints, + address _receiver + ) external; +} diff --git a/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol new file mode 100644 index 000000000..97a125174 --- /dev/null +++ b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.5.17; + +import "../governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol"; + +contract FeeSharingCollectorMultipleTokenMockup is FeeSharingCollectorMultipleToken { + struct TestData { + address loanPoolToken; + uint32 maxCheckpoints; + address receiver; + } + + TestData public testData; + + constructor(IProtocol _protocol, IStaking _staking) public { + protocol = _protocol; + staking = _staking; + } + + function withdraw(address _token, uint32 _maxCheckpoint, address _receiver) public { + testData = TestData(_token, _maxCheckpoint, _receiver); + } + + function trueWithdraw(address _token, uint32 _maxCheckpoint, address _receiver) public { + super.withdraw(_token, _maxCheckpoint, _receiver); + } + + function trueWithdrawTokens( + address[] memory _tokens, + uint32[] memory _maxCheckpoints, + address _receiver + ) public { + super.withdrawTokens(_tokens, _maxCheckpoints, _receiver); + } + + function addCheckPoint(address loanPoolToken, uint256 poolTokenAmount) public { + uint96 amount96 = safe96( + poolTokenAmount, + "FeeSharingProxy::withdrawFees: pool token amount exceeds 96 bits" + ); + _addCheckpoint(loanPoolToken, amount96); + } +} diff --git a/contracts/mockup/MockSovrynDexMultipleToken.sol b/contracts/mockup/MockSovrynDexMultipleToken.sol new file mode 100644 index 000000000..378be1dc4 --- /dev/null +++ b/contracts/mockup/MockSovrynDexMultipleToken.sol @@ -0,0 +1,37 @@ +pragma solidity 0.5.17; + +import "../interfaces/IERC20.sol"; +import "../governance/IFeeSharingCollectorMultipleToken.sol"; + +contract MockSovrynDexMultipleToken { + mapping(address => uint96) public tokenFees; + uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; + uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; + IERC20 wrbtcToken; + address treasury; + + constructor() public {} + + function setTreasury(address _treasury) public { + treasury = _treasury; + } + + function setWrbtcToken(IERC20 _wrbtcToken) public { + wrbtcToken = _wrbtcToken; + } + + function userCmd(uint16 callpath, bytes calldata cmd) external payable { + require(msg.sender == treasury, "Only Treasury"); + (uint8 cmdCode, address token) = abi.decode(cmd, (uint8, address)); + if ( + callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && + cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE + ) { + IFeeSharingCollectorMultipleToken(treasury).transferTokens(token, tokenFees[token]); + } + } + + function setTokenDexFee(address token, uint96 fee) external { + tokenFees[token] = fee; + } +} diff --git a/tests/FeeSharingCollectorMultipleToken.test.js b/tests/FeeSharingCollectorMultipleToken.test.js new file mode 100644 index 000000000..ee547e1bf --- /dev/null +++ b/tests/FeeSharingCollectorMultipleToken.test.js @@ -0,0 +1,2947 @@ +const { expect } = require("chai"); +const { loadFixture, takeSnapshot, mine } = require("@nomicfoundation/hardhat-network-helpers"); +const { expectRevert, expectEvent, constants, BN } = require("@openzeppelin/test-helpers"); + +const { ZERO_ADDRESS } = constants; + +const { etherMantissa, mineBlock, increaseTime } = require("./Utils/Ethereum"); + +const { + deployAndGetIStaking, + replaceStakingModule, + getStakingModulesObject, + getStakingModulesAddressList, +} = require("./Utils/initializer"); + +const TestToken = artifacts.require("TestToken"); + +const StakingProxy = artifacts.require("StakingProxy"); +const VestingLogic = artifacts.require("VestingLogicMockup"); +const Vesting = artifacts.require("TeamVesting"); + +const ISovryn = artifacts.require("ISovryn"); +const Affiliates = artifacts.require("Affiliates"); + +const Protocol = artifacts.require("sovrynProtocol"); +const ProtocolSettings = artifacts.require("ProtocolSettingsMockup"); +const LoanMaintenance = artifacts.require("LoanMaintenance"); +const LoanSettings = artifacts.require("LoanSettings"); +const LoanClosingsLiquidation = artifacts.require("LoanClosingsLiquidation"); +const LoanClosingsRollover = artifacts.require("LoanClosingsRollover"); +const LoanClosingsWith = artifacts.require("LoanClosingsWith"); + +const ILoanTokenLogicProxy = artifacts.require("ILoanTokenLogicProxy"); +const ILoanTokenModules = artifacts.require("ILoanTokenModules"); +const LoanTokenLogicWrbtc = artifacts.require("LoanTokenLogicWrbtc"); +const LoanToken = artifacts.require("LoanToken"); +const LockedSOV = artifacts.require("LockedSOV"); + +const FeeSharingCollector = artifacts.require("FeeSharingCollectorMultipleToken"); +const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); +const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMultipleTokenMockup"); +const MockSovrynDex = artifacts.require("MockSovrynDexMultipleToken"); +const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup"); +const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup"); + +const PriceFeedsLocal = artifacts.require("PriceFeedsLocal"); + +const VestingFactory = artifacts.require("VestingFactory"); +const VestingRegistry = artifacts.require("VestingRegistry3"); + +const SwapsImplSovrynSwapLib = artifacts.require("SwapsImplSovrynSwapLib"); +const SwapsImplSovrynSwap = artifacts.require("SwapsImplSovrynSwapModule"); +const TestSovrynSwap = artifacts.require("TestSovrynSwap"); +const SwapsExternal = artifacts.require("SwapsExternal"); + +const TOTAL_SUPPLY = etherMantissa(1000000000); + +const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092)); +const TWO_WEEKS = 1209600; + +const MAX_VOTING_WEIGHT = 10; + +const FEE_WITHDRAWAL_INTERVAL = 172800; + +const MOCK_PRIOR_WEIGHTED_STAKE = false; + +const wei = web3.utils.toWei; + +const { lend_btc_before_cashout } = require("./loan-token/helpers"); + +const mutexUtils = require("../deployment/helpers/reentrancy/utils"); + +let cliff = 1; // This is in 4 weeks. i.e. 1 * 4 weeks. +let duration = 11; // This is in 4 weeks. i.e. 11 * 4 weeks. + +const { + getSUSD, + getRBTC, + getWRBTC, + getBZRX, + getLoanTokenLogic, + getLoanToken, + getLoanTokenLogicWrbtc, + getLoanTokenWRBTC, + loan_pool_setup, + set_demand_curve, + getPriceFeeds, + getSovryn, + decodeLogs, + getSOV, +} = require("./Utils/initializer.js"); + +contract("FeeSharingCollectorMultipleToken:", (accounts) => { + const name = "Test SOVToken"; + const symbol = "TST"; + + let root, account1, account2, account3, account4; + let SOVToken, SUSD, WrappedNativeToken, sovryn, staking; + let loanTokenSettings, loanTokenLogic, loanToken; + let feeSharingCollectorProxyObj; + let feeSharingCollector; + let feeSharingCollectorLogic; + let loanWrappedNativeToken; + let tradingFeePercent; + let mockPrice; + let sovrynDex; + + before(async () => { + [root, account1, account2, account3, account4, ...accounts] = accounts; + + try { + /** Deploy SwapsImplSovrynSwapLib */ + const swapsImplSovrynSwapLib = await SwapsImplSovrynSwapLib.new(); + await LoanMaintenance.link(swapsImplSovrynSwapLib); + await SwapsExternal.link(swapsImplSovrynSwapLib); + await LoanClosingsWith.link(swapsImplSovrynSwapLib); + await LoanClosingsRollover.link(swapsImplSovrynSwapLib); + await SwapsImplSovrynSwap.link(swapsImplSovrynSwapLib); + } catch (err) {} + }); + + async function protocolDeploymentFixture(_wallets, _provider) { + // Need to deploy the mutex in the initialization. Otherwise, the global reentrancy prevention will not be working & throw an error. + await mutexUtils.getOrDeployMutex(); + + // Token + SOVToken = await TestToken.new(name, symbol, 18, TOTAL_SUPPLY); + + // Staking + // Creating the Staking Instance (Staking Modules Interface). + const stakingProxy = await StakingProxy.new(SOVToken.address); + const modulesObject = await getStakingModulesObject(); + + staking = await deployAndGetIStaking(stakingProxy.address, modulesObject); + + const weightedStakingModuleMockup = await WeightedStakingModuleMockup.new(); + const modulesAddressList = getStakingModulesAddressList(modulesObject); + + await replaceStakingModule( + stakingProxy.address, + modulesAddressList["WeightedStakingModule"], + weightedStakingModuleMockup.address + ); + + iWeightedStakingModuleMockup = await IWeightedStakingModuleMockup.at(staking.address); + + SUSD = await getSUSD(); + RBTC = await getRBTC(); + WrappedNativeToken = await getWRBTC(); + BZRX = await getBZRX(); + priceFeeds = await getPriceFeeds(WrappedNativeToken, SUSD, RBTC, BZRX); + + // Deploying sovrynProtocol w/ generic function from initializer.js + /// @dev Tried but no success so far. When using the getSovryn function + /// , contracts revert w/ "target not active" error. + /// The weird thing is that deployment code below is exactly the same as + /// the code from getSovryn function at initializer.js. + /// Inline code works ok, but when calling the function it does not. + // sovryn = await getSovryn(WRBTC, SUSD, RBTC, priceFeeds); + // await sovryn.setSovrynProtocolAddress(sovryn.address); + + const sovrynproxy = await Protocol.new(); + sovryn = await ISovryn.at(sovrynproxy.address); + + await sovryn.replaceContract((await ProtocolSettings.new()).address); + await sovryn.replaceContract((await LoanSettings.new()).address); + await sovryn.replaceContract((await LoanMaintenance.new()).address); + await sovryn.replaceContract((await SwapsExternal.new()).address); + + await sovryn.setWrbtcToken(WrappedNativeToken.address); + + await sovryn.replaceContract((await LoanClosingsWith.new()).address); + await sovryn.replaceContract((await LoanClosingsLiquidation.new()).address); + await sovryn.replaceContract((await LoanClosingsRollover.new()).address); + + await sovryn.replaceContract((await Affiliates.new()).address); + + sovryn = await ProtocolSettings.at(sovryn.address); + + // Loan token + const initLoanTokenLogic = await getLoanTokenLogic(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon] + loanTokenLogic = initLoanTokenLogic[0]; + loanTokenLogicBeacon = initLoanTokenLogic[1]; + + loanToken = await LoanToken.new( + root, + loanTokenLogic.address, + sovryn.address, + WrappedNativeToken.address + ); + await loanToken.initialize(SUSD.address, "iSUSD", "iSUSD"); + + /** Initialize the loan token logic proxy */ + loanToken = await ILoanTokenLogicProxy.at(loanToken.address); + await loanToken.setBeaconAddress(loanTokenLogicBeacon.address); + + /** Use interface of LoanTokenModules */ + loanToken = await ILoanTokenModules.at(loanToken.address); + + await loanToken.setAdmin(root); + await sovryn.setLoanPool([loanToken.address], [SUSD.address]); + + // FeeSharingCollector + feeSharingCollectorLogic = await FeeSharingCollector.new(); + feeSharingCollectorProxyObj = await FeeSharingCollectorProxy.new( + sovryn.address, + staking.address + ); + await feeSharingCollectorProxyObj.setImplementation(feeSharingCollectorLogic.address); + feeSharingCollector = await FeeSharingCollector.at(feeSharingCollectorProxyObj.address); + + await sovryn.setFeesController(feeSharingCollector.address); + + // Set loan pool for wrappedNativeToken -- because our fee sharing proxy required the loanPool of wrappedNativeToken + // Loan token + const initLoanTokenLogicWrbtc = await getLoanTokenLogicWrbtc(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon] + loanTokenLogicWrbtc = initLoanTokenLogicWrbtc[0]; + loanTokenLogicBeaconWrbtc = initLoanTokenLogicWrbtc[1]; + + loanWrappedNativeToken = await LoanToken.new( + root, + loanTokenLogicWrbtc.address, + sovryn.address, + WrappedNativeToken.address + ); + await loanWrappedNativeToken.initialize( + WrappedNativeToken.address, + "iWrappedNativeToken", + "iWrappedNativeToken" + ); + + /** Initialize the loan token logic proxy */ + loanWrappedNativeToken = await ILoanTokenLogicProxy.at(loanWrappedNativeToken.address); + await loanWrappedNativeToken.setBeaconAddress(loanTokenLogicBeaconWrbtc.address); + + /** Use interface of LoanTokenModules */ + loanWrappedNativeToken = await ILoanTokenModules.at(loanWrappedNativeToken.address); + + const loanTokenAddressWrappedNativeToken = await loanWrappedNativeToken.loanTokenAddress(); + await sovryn.setLoanPool( + [loanWrappedNativeToken.address], + [loanTokenAddressWrappedNativeToken] + ); + + await WrappedNativeToken.mint(sovryn.address, wei("500", "ether")); + + await sovryn.setWrbtcToken(WrappedNativeToken.address); + await sovryn.setSOVTokenAddress(SOVToken.address); + await sovryn.setSovrynProtocolAddress(sovryn.address); + + // Creating the Vesting Instance. + vestingLogic = await VestingLogic.new(); + vestingFactory = await VestingFactory.new(vestingLogic.address); + vestingRegistry = await VestingRegistry.new( + vestingFactory.address, + SOVToken.address, + staking.address, + feeSharingCollector.address, + root // This should be Governance Timelock Contract. + ); + vestingFactory.transferOwnership(vestingRegistry.address); + + await sovryn.setLockedSOVAddress( + ( + await LockedSOV.new(SOVToken.address, vestingRegistry.address, cliff, duration, [ + root, + ]) + ).address + ); + + // Set PriceFeeds + feeds = await PriceFeedsLocal.new(WrappedNativeToken.address, sovryn.address); + mockPrice = "1"; + await feeds.setRates(SUSD.address, WrappedNativeToken.address, wei(mockPrice, "ether")); + const swaps = await SwapsImplSovrynSwap.new(); + const sovrynSwapSimulator = await TestSovrynSwap.new(feeds.address); + await sovryn.setSovrynSwapContractRegistryAddress(sovrynSwapSimulator.address); + await sovryn.setSupportedTokens([SUSD.address, WrappedNativeToken.address], [true, true]); + await sovryn.setPriceFeedContract( + feeds.address // priceFeeds + ); + await sovryn.setSwapsImplContract( + swaps.address // swapsImpl + ); + + tradingFeePercent = await sovryn.tradingFeePercent(); + await lend_btc_before_cashout(loanWrappedNativeToken, new BN(wei("10", "ether")), root); + + const maxDisagreement = new BN(wei("5", "ether")); + await sovryn.setMaxDisagreement(maxDisagreement); + + sovrynDex = await MockSovrynDex.new(); + + await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + + return sovryn; + } + + beforeEach(async () => { + await loadFixture(protocolDeploymentFixture); + }); + + describe("initialization", async () => { + it("revert if initialize called by non-owner account", async () => { + await expectRevert( + feeSharingCollector.initialize( + WrappedNativeToken.address, + loanWrappedNativeToken.address, + { + from: account3, + } + ), + "unauthorized" + ); + }); + + it("revert if setWrappedNativeToken called by non-owner account", async () => { + await expectRevert( + feeSharingCollector.setWrappedNativeToken(WrappedNativeToken.address, { + from: account3, + }), + "unauthorized" + ); + }); + + it("should revert if initialized more than once", async () => { + const wrappedNativeTokenAddress = ( + await TestToken.new("WrappedNativeToken", "WNT", 18, 100) + ).address; + const loanWrappedNativeTokenAddress = ( + await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100) + ).address; + await expectRevert( + feeSharingCollector.initialize(wrappedNativeTokenAddress, sovrynDex.address), + "function can only be called once" + ); + }); + + it("setSovrynDexAddress should only be called once", async () => { + expect(await feeSharingCollector.sovrynDexAddress()).to.equal(sovrynDex.address); + const newSovrynDexAddress = (await MockSovrynDex.new()).address; + await expectRevert( + feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), + "FeeSharingCollector: function can only be called once" + ); + }); + }); + + describe("FeeSharingCollectorProxy", () => { + before(async () => { + await loadFixture(protocolDeploymentFixture); + }); + beforeEach(async () => { + snapshot = await takeSnapshot(); + }); + afterEach(async () => { + await snapshot.restore(); + }); + + it("Check owner & implementation", async () => { + const proxyOwner = await feeSharingCollectorProxyObj.getProxyOwner(); + const implementation = await feeSharingCollectorProxyObj.getImplementation(); + + expect(implementation).to.be.equal(feeSharingCollectorLogic.address); + expect(proxyOwner).to.be.equal(root); + }); + + it("Set new implementation", async () => { + const newFeeSharingCollector = await FeeSharingCollector.new(); + await feeSharingCollectorProxyObj.setImplementation(newFeeSharingCollector.address); + const newImplementation = await feeSharingCollectorProxyObj.getImplementation(); + + expect(newImplementation).to.be.equal(newFeeSharingCollector.address); + }); + }); + + describe("withdrawFees", () => { + it("Shouldn't be able to use zero token address", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.withdrawFees([ZERO_ADDRESS]), + "FeeSharingCollectorMultipleToken::withdrawFees: token is not a contract" + ); + }); + + it("Shouldn't be able to withdraw if wRBTC loan pool does not exist", async () => { + await protocolDeploymentFixture(); + // Unset the loanPool for wRBTC + await sovryn.setLoanPool([loanWrappedNativeToken.address], [ZERO_ADDRESS]); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + true + ); + + await expectRevert( + feeSharingCollector.withdrawFees([WrappedNativeToken.address]), + "FeeSharingCollectorMultipleToken::withdrawFees: loan wRBTC not found" + ); + }); + + it("Shouldn't be able to withdraw zero amount", async () => { + await protocolDeploymentFixture(); + const tx = await feeSharingCollector.withdrawFees([SUSD.address]); + expectEvent(tx, "FeeWithdrawn", { + sender: root, + token: loanWrappedNativeToken.address, + amount: new BN(0), + }); + }); + + it("ProtocolSettings.withdrawFees", async () => { + /// @dev This test requires redeploying the protocol + const protocol = await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let previousProtocolWrbtcBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); + // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + await protocol.setFeesController(root); + let tx = await protocol.withdrawFees([SUSD.address], root); + let latestProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(protocol.address); + + await checkWithdrawFee(); + + //check wrappedNativeToken balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let userBalance = await WrappedNativeToken.balanceOf.call(root); + expect(userBalance.toString()).to.be.equal(feeAmount.toString()); + + // wrappedNativeToken balance should remain the same + expect(previousProtocolWrbtcBalance.toString()).to.equal( + latestProtocolWrbtcBalance.toString() + ); + + expectEvent(tx, "WithdrawFees", { + sender: root, + token: SUSD.address, + receiver: root, + lendingAmount: lendingFeeTokensHeld, + tradingAmount: tradingFeeTokensHeld, + borrowingAmount: borrowingFeeTokensHeld, + // amountConvertedToWRBTC + }); + }); + + it("ProtocolSettings.withdrawFees (WRBTC token)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + true + ); + // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + await sovryn.setFeesController(root); + let tx = await sovryn.withdrawFees([WrappedNativeToken.address], account1); + + await checkWithdrawFee(true, true, false); + + //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let userBalance = await WrappedNativeToken.balanceOf.call(account1); + expect(userBalance.toString()).to.be.equal(feeAmount.toString()); + + expectEvent(tx, "WithdrawFees", { + sender: root, + token: WrappedNativeToken.address, + receiver: account1, + lendingAmount: lendingFeeTokensHeld, + tradingAmount: tradingFeeTokensHeld, + borrowingAmount: borrowingFeeTokensHeld, + }); + }); + + /// @dev Test coverage + it("ProtocolSettings.withdrawFees: Revert withdrawing by no feesController", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + // mock data + let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + + await sovryn.setFeesController(root); + + await expectRevert( + sovryn.withdrawFees([SUSD.address], account1, { from: account1 }), + "unauthorized" + ); + }); + + it("Should be able to withdraw fees", async () => { + /// @dev This test requires redeploying the protocol + const protocol = await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let previousProtocolWrbtcBalance = await WrappedNativeToken.balanceOf( + protocol.address + ); + + tx = await feeSharingCollector.withdrawFees([SUSD.address]); + + await checkWithdrawFee(); + + //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString()); + + // wrappedNativeToken balance should remain the same + let latestProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(protocol.address); + expect(previousProtocolWrbtcBalance.toString()).to.equal( + latestProtocolWrbtcBalance.toString() + ); + + //checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + loanWrappedNativeToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call( + loanWrappedNativeToken.address, + 0 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); + + // check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + loanWrappedNativeToken.address + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + expectEvent(tx, "FeeWithdrawn", { + sender: root, + token: loanWrappedNativeToken.address, + amount: feeAmount, + }); + }); + + it("Should be able to withdraw fees (WRBTC token)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + true + ); + + tx = await feeSharingCollector.withdrawFees([WrappedNativeToken.address]); + + await checkWithdrawFee(); + + //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString()); + + //checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + loanWrappedNativeToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call( + loanWrappedNativeToken.address, + 0 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); + + //check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + loanWrappedNativeToken.address + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + expectEvent(tx, "FeeWithdrawn", { + sender: root, + token: loanWrappedNativeToken.address, + amount: feeAmount, + }); + }); + + it("Should be able to withdraw fees (sov token)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + false, + true + ); + tx = await feeSharingCollector.withdrawFees([SOVToken.address]); + + await checkWithdrawFee(false, false, true); + + //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingProxyBalance = await SOVToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString()); + + //checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + SOVToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 0); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); + + //check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + SOVToken.address + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + expectEvent(tx, "TokensTransferred", { + sender: sovryn.address, + token: SOVToken.address, + amount: feeAmount, + }); + }); + + it("Should be able to withdraw fees 3 times", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(1000, root); + + // [FIRST] + // mock data + let mockAmountLendingFeeTokensHeld = 0; + let mockAmountTradingFeeTokensHeld = 1; + let mockAmountBorrowingFeeTokensHeld = 2; + let totalMockAmount1 = + mockAmountLendingFeeTokensHeld + + mockAmountTradingFeeTokensHeld + + mockAmountBorrowingFeeTokensHeld; + let lendingFeeTokensHeld = new BN(mockAmountLendingFeeTokensHeld); + let tradingFeeTokensHeld = new BN( + wei(mockAmountTradingFeeTokensHeld.toString(), "ether") + ); + let borrowingFeeTokensHeld = new BN( + wei(mockAmountBorrowingFeeTokensHeld.toString(), "ether") + ); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let totalFeeAmount = feeAmount; + + let tx = await feeSharingCollector.withdrawFees([SUSD.address]); + + await checkWithdrawFee(); + + // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString()); + + // checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + loanWrappedNativeToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call( + loanWrappedNativeToken.address, + 0 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); + + // check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + loanWrappedNativeToken.address + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + // [SECOND] + // mock data + let mockAmountLendingFeeTokensHeld2 = 1; + let mockAmountTradingFeeTokensHeld2 = 0; + let mockAmountBorrowingFeeTokensHeld2 = 0; + let totalMockAmount2 = + mockAmountTradingFeeTokensHeld2 + + mockAmountBorrowingFeeTokensHeld2 + + mockAmountLendingFeeTokensHeld2; + lendingFeeTokensHeld = new BN( + wei(mockAmountLendingFeeTokensHeld2.toString(), "ether") + ); + tradingFeeTokensHeld = new BN(mockAmountTradingFeeTokensHeld2); + borrowingFeeTokensHeld = new BN(mockAmountBorrowingFeeTokensHeld2); + totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let unprocessedAmount = feeAmount; + totalFeeAmount = totalFeeAmount.add(feeAmount); + + tx = await feeSharingCollector.withdrawFees([SUSD.address]); + + // Need to checkwithdrawfee manually + await checkWithdrawFee(); + + // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(totalFeeAmount.toString()); + + // [THIRD] + // mock data + let mockAmountLendingFeeTokensHeld3 = 0; + let mockAmountTradingFeeTokensHeld3 = 0.5; + let mockAmountBorrowingFeeTokensHeld3 = 0.5; + let totalMockAmount3 = + mockAmountTradingFeeTokensHeld3 + + mockAmountBorrowingFeeTokensHeld3 + + mockAmountLendingFeeTokensHeld3; + lendingFeeTokensHeld = new BN(mockAmountLendingFeeTokensHeld3); + tradingFeeTokensHeld = new BN( + wei(mockAmountTradingFeeTokensHeld3.toString(), "ether") + ); + borrowingFeeTokensHeld = new BN( + wei(mockAmountBorrowingFeeTokensHeld3.toString(), "ether") + ); + totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + totalFeeAmount = totalFeeAmount.add(feeAmount); + + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + tx = await feeSharingCollector.withdrawFees([SUSD.address]); + // In this state the price of SUSD/WRBTC already adjusted because of previous swap, so we need to consider this in the next swapFee calculation + await checkWithdrawFee(); + + // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee) + feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toString()).to.be.equal(totalFeeAmount.toString()); + + // checkpoints + totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + loanWrappedNativeToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); + checkpoint = await feeSharingCollector.tokenCheckpoints.call( + loanWrappedNativeToken.address, + 1 + ); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal( + feeAmount.add(unprocessedAmount).toString() + ); + + // check lastFeeWithdrawalTime + lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + loanWrappedNativeToken.address + ); + block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + // make sure wrappedNativeToken balance is 0 after withdrawal + let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString()); + }); + }); + + describe("transferTokens", () => { + it("Shouldn't be able to use zero token address", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.transferTokens(ZERO_ADDRESS, 1000), + "FeeSharingCollectorMultipleToken::transferTokens: invalid address" + ); + }); + + it("Shouldn't be able to transfer zero amount", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.transferTokens(SOVToken.address, 0), + "FeeSharingCollectorMultipleToken::transferTokens: invalid amount" + ); + }); + + it("Shouldn't be able to withdraw zero amount", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.transferTokens(SOVToken.address, 1000), + "invalid transfer" + ); + }); + + it("Should be able to transfer tokens", async () => { + await protocolDeploymentFixture(); + // stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + let amount = 1000; + await SOVToken.approve(feeSharingCollector.address, amount * 7); + + let tx = await feeSharingCollector.transferTokens(SOVToken.address, amount); + + expect( + await feeSharingCollector.unprocessedAmount.call(SOVToken.address) + ).to.be.bignumber.equal(new BN(0)); + + expectEvent(tx, "TokensTransferred", { + sender: root, + token: SOVToken.address, + amount: new BN(amount), + }); + + // checkpoints + let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + SOVToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); + let checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 0); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toString()).to.be.equal(amount.toString()); + + // check lastFeeWithdrawalTime + let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + SOVToken.address + ); + let block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + + expectEvent(tx, "CheckpointAdded", { + sender: root, + token: SOVToken.address, + amount: new BN(amount), + }); + + // second time + tx = await feeSharingCollector.transferTokens(SOVToken.address, amount * 2); + + expect( + await feeSharingCollector.unprocessedAmount.call(SOVToken.address) + ).to.be.bignumber.equal(new BN(amount * 2)); + + expectEvent(tx, "TokensTransferred", { + sender: root, + token: SOVToken.address, + amount: new BN(amount * 2), + }); + + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + // third time + tx = await feeSharingCollector.transferTokens(SOVToken.address, amount * 4); + + expect( + await feeSharingCollector.unprocessedAmount.call(SOVToken.address) + ).to.be.bignumber.equal(new BN(0)); + + // checkpoints + totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( + SOVToken.address + ); + expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); + checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 1); + expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); + expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( + totalStake * MAX_VOTING_WEIGHT + ); + expect(checkpoint.numTokens.toNumber()).to.be.equal(amount * 6); + + // check lastFeeWithdrawalTime + lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( + SOVToken.address + ); + block = await web3.eth.getBlock(tx.receipt.blockNumber); + expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); + }); + }); + + describe("withdraw", () => { + it("Shouldn't be able to withdraw without checkpoints (for token pool)", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.withdraw(loanToken.address, 0, account2, { from: account1 }), + "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + ); + }); + + it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.withdraw(loanWrappedNativeToken.address, 0, account2, { + from: account1, + }), + "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + ); + }); + + it("Shouldn't be able to withdraw zero amount (for token pool)", async () => { + await protocolDeploymentFixture(); + let fees = await feeSharingCollector.getAccumulatedFees(account1, loanToken.address); + expect(fees).to.be.bignumber.equal("0"); + + await expectRevert( + feeSharingCollector.withdraw(loanToken.address, 10, ZERO_ADDRESS, { + from: account1, + }), + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + }); + + it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => { + await protocolDeploymentFixture(); + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal("0"); + + await expectRevert( + feeSharingCollector.withdraw(loanWrappedNativeToken.address, 10, ZERO_ADDRESS, { + from: account1, + }), + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + }); + + it("Should be able to withdraw to another account", async () => { + await protocolDeploymentFixture(); + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); + + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 1000, + account2, + { + from: account1, + } + ); + + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account2, + token: loanWrappedNativeToken.address, + amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (token pool)", async () => { + await protocolDeploymentFixture(); + // FeeSharingCollector + feeSharingCollector = await FeeSharingCollectorMockup.new( + sovryn.address, + staking.address + ); + await sovryn.setFeesController(feeSharingCollector.address); + + await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // Mock (transfer loanToken to FeeSharingProxy contract) + const loanPoolTokenAddress = await sovryn.underlyingToLoanPool(SUSD.address); + const amountLend = new BN(wei("500", "ether")); + await SUSD.approve(loanPoolTokenAddress, amountLend); + await loanToken.mint(feeSharingCollector.address, amountLend); + + // Check ISUSD Balance for feeSharingProxy + const feeSharingProxyLoanBalanceToken = await loanToken.balanceOf( + feeSharingCollector.address + ); + expect(feeSharingProxyLoanBalanceToken.toString()).to.be.equal(amountLend.toString()); + + // Withdraw ISUSD from feeSharingProxy + // const initial + await feeSharingCollector.addCheckPoint(loanPoolTokenAddress, amountLend.toString()); + let tx = await feeSharingCollector.trueWithdrawTokens( + [loanToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ); + const updatedFeeSharingProxyLoanBalanceToken = await loanToken.balanceOf( + feeSharingCollector.address + ); + const updatedAccount1LoanBalanceToken = await loanToken.balanceOf(account1); + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + + expect(updatedFeeSharingProxyLoanBalanceToken.toString()).to.be.equal( + ((amountLend * 7) / 10).toString() + ); + expect(updatedAccount1LoanBalanceToken.toString()).to.be.equal( + ((amountLend * 3) / 10).toString() + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: loanToken.address, + amount: amountLend.mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (WRBTC pool)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 10, + ZERO_ADDRESS, + { + from: account1, + } + ); + + /// @dev To anticipate gas consumption it is required to split hardhat + /// behaviour into two different scenarios: coverage and regular testing. + /// On coverage gasPrice = 1, on regular tests gasPrice = 8000000000 + // + // On coverage: + // Fees: 1800000000 + // Balance: 10000000000000000000000 + // Balance: 10000000000001799398877 + // withdraw().gasUsed: 601123 + // txFee: 601123 + // + // On regular test: + // Fees: 1800000000 + // Balance: 10000000000000000000000 + // Balance: 9999996433281800000000 + // withdraw().gasUsed: 445840 + // txFee: 3566720000000000 + let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + let gasPrice; + /// @dev A balance decrease (negative difference) corresponds to regular test case + if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") { + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + } // regular test + else { + gasPrice = new BN(1); + } // coverage + + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + // check balances + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userLoanTokenBalance.toNumber()).to.be.equal(0); + let userExpectedBtcBalance = userInitialBtcBalance.add( + feeAmount.mul(new BN(3)).div(new BN(10)) + ); + expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString()); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: loanWrappedNativeToken.address, + amount: feeAmount.mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (sov pool)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + false, + true + ); + + await feeSharingCollector.withdrawFees([SOVToken.address]); + + let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + let userInitialISOVBalance = await SOVToken.balanceOf(account1); + let tx = await feeSharingCollector.withdraw(SOVToken.address, 10, ZERO_ADDRESS, { + from: account1, + }); + + //processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + SOVToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + //check balances + let feeSharingProxyBalance = await SOVToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + let userBalance = await SOVToken.balanceOf.call(account1); + expect(userBalance.sub(userInitialISOVBalance).toNumber()).to.be.equal( + (feeAmount * 3) / 10 + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: SOVToken.address, + amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (sov pool) to another account", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + false, + true + ); + + await feeSharingCollector.withdrawFees([SOVToken.address]); + + let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + const receiverBalanceBefore = await SOVToken.balanceOf(account2); + let tx = await feeSharingCollector.withdraw(SOVToken.address, 10, account2, { + from: account1, + }); + + //processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + SOVToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + //check balances + let feeSharingProxyBalance = await SOVToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + const receiverBalanceAfter = await SOVToken.balanceOf(account2); + const amountWithdrawn = new BN(feeAmount).mul(new BN(3)).div(new BN(10)); + expect(receiverBalanceAfter.sub(receiverBalanceBefore).toString()).to.be.equal( + amountWithdrawn.toString() + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account2, + token: SOVToken.address, + amount: amountWithdrawn, + }); + }); + + it("Should be able to withdraw using 3 checkpoints", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 900; + await stake(rootStake, root); + + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // [FIRST] + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let totalFeeAmount = feeAmount; + await feeSharingCollector.withdrawFees([SUSD.address]); + + let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 1, + ZERO_ADDRESS, + { + from: account1, + } + ); + + /// @dev Same as above gas consumption is different on regular tests than on coverge + let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + let gasPrice; + /// @dev A balance decrease (negative difference) corresponds to regular test case + if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") { + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + } // regular test + else { + gasPrice = new BN(1); + } // coverage + + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + // check balances + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((totalFeeAmount * 9) / 10); + let userBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userBalance.toNumber()).to.be.equal(0); + + expect(userLatestBTCBalance.toString()).to.be.equal( + userInitialBtcBalance.add(totalFeeAmount.mul(new BN(1)).div(new BN(10))).toString() + ); + + // [SECOND] + // mock data + let lendingFeeTokensHeld2 = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld2 = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld2 = new BN(wei("3", "gwei")); + totalFeeTokensHeld = lendingFeeTokensHeld2 + .add(tradingFeeTokensHeld2) + .add(borrowingFeeTokensHeld2); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld2, + tradingFeeTokensHeld2, + borrowingFeeTokensHeld2 + ); + totalFeeAmount = totalFeeAmount.add(feeAmount); + let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount; + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + await feeSharingCollector.withdrawFees([SUSD.address]); + + // [THIRD] + // mock data + let lendingFeeTokensHeld3 = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld3 = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld3 = new BN(wei("3", "gwei")); + totalFeeTokensHeld = lendingFeeTokensHeld3 + .add(tradingFeeTokensHeld3) + .add(borrowingFeeTokensHeld3); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld3, + tradingFeeTokensHeld3, + borrowingFeeTokensHeld3 + ); + totalFeeAmount = totalFeeAmount.add(feeAmount); + totalLoanTokenWRBTCBalanceShouldBeAccount1 = + totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount); + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + await feeSharingCollector.withdrawFees([SUSD.address]); + + // [SECOND] - [THIRD] + userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 2, + ZERO_ADDRESS, + { + from: account1, + } + ); + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed); + txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(3); + + // check balances + feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal( + parseInt((totalFeeAmount * 9) / 10) + ); + userBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userBalance.toNumber()).to.be.equal(0); + + userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + + expect(userLatestBTCBalance.toString()).to.be.equal( + userInitialBtcBalance + .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10))) + .toString() + ); + }); + + it("Should be able to process 10 checkpoints", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + await stake(900, root); + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + await createCheckpoints(10); + + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 1000, + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 10).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(10); + }); + + it("Should be able to process 10 checkpoints and 3 withdrawals", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + await stake(900, root); + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + await createCheckpoints(10); + + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 5, + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 5).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(5); + + tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 3, + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 3).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(8); + + tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 1000, + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(10); + }); + + // // use for gas usage tests + // it("Should be able to process 30 checkpoints", async () => { + // // stake - getPriorTotalVotingPower + // await stake(900, root); + // let userStake = 100; + // if (MOCK_PRIOR_WEIGHTED_STAKE) { + // await staking.MOCK_priorWeightedStake(userStake * 10); + // } + // await SOVToken.transfer(account1, userStake); + // await stake(userStake, account1); + // + // // mock data + // await createCheckpoints(30); + // + // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1}); + // console.log("\nwithdraw(checkpoints = 30).gasUsed: " + tx.receipt.gasUsed); + // // processedCheckpoints + // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address); + // expect(processedCheckpoints.toNumber()).to.be.equal(30); + // }); + // + // // use for gas usage tests + // it("Should be able to process 100 checkpoints", async () => { + // // stake - getPriorTotalVotingPower + // await stake(900, root); + // let userStake = 100; + // if (MOCK_PRIOR_WEIGHTED_STAKE) { + // await staking.MOCK_priorWeightedStake(userStake * 10); + // } + // await SOVToken.transfer(account1, userStake); + // await stake(userStake, account1); + // + // // mock data + // await createCheckpoints(100); + // + // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1}); + // console.log("\nwithdraw(checkpoints = 500).gasUsed: " + tx.receipt.gasUsed); + // // processedCheckpoints + // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address); + // expect(processedCheckpoints.toNumber()).to.be.equal(100); + // }); + // + // // use for gas usage tests + // it("Should be able to withdraw when staking contains a lot of checkpoints", async () => { + // let checkpointCount = 1000; + // await stake(1000, root, checkpointCount); + // let afterBlock = await blockNumber(); + // console.log(afterBlock); + // + // let kickoffTS = await staking.kickoffTS.call(); + // let stakingDate = kickoffTS.add(new BN(MAX_DURATION)); + // + // let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints.call(root, stakingDate); + // let firstCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, 0); + // let lastCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, numUserStakingCheckpoints - 1); + // let block1 = firstCheckpoint.fromBlock.toNumber() + 1; + // let block2 = lastCheckpoint.fromBlock; + // + // console.log("numUserStakingCheckpoints = " + numUserStakingCheckpoints.toString()); + // console.log("first = " + firstCheckpoint.fromBlock.toString()); + // console.log("last = " + lastCheckpoint.fromBlock.toString()); + // + // let tx = await staking.calculatePriorWeightedStake(root, block1, stakingDate); + // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed); + // tx = await staking.calculatePriorWeightedStake(root, block2, stakingDate); + // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed); + // }); + + it("Should be able to withdraw with staking for 78 dates", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + let kickoffTS = await staking.kickoffTS.call(); + await SOVToken.approve(staking.address, userStake * 1000); + for (let i = 0; i < 77; i++) { + let stakingDate = kickoffTS.add(new BN(TWO_WEEKS * (i + 1))); + await staking.stake(userStake, stakingDate, account1, account1); + } + + // mock data + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let tx = await feeSharingCollector.withdraw( + loanWrappedNativeToken.address, + 10, + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + }); + + it("should compute the weighted stake and show gas usage", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + await stake(100, root); + let kickoffTS = await staking.kickoffTS.call(); + let stakingDate = kickoffTS.add(new BN(MAX_DURATION)); + await SOVToken.approve(staking.address, 100); + let result = await staking.stake("100", stakingDate, root, root); + await mineBlock(); + + let tx = await iWeightedStakingModuleMockup.calculatePriorWeightedStake( + root, + result.receipt.blockNumber, + stakingDate + ); + console.log("\ngasUsed: " + tx.receipt.gasUsed); + }); + }); + + describe("withdrawTokens", () => { + it("Shouldn't be able to withdraw without checkpoints (for token pool)", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.withdrawTokens([loanToken.address], [0], account2, { + from: account1, + }), + "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + ); + }); + + it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => { + await protocolDeploymentFixture(); + await expectRevert( + feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [0], + account2, + { from: account1 } + ), + "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + ); + }); + + it("Shouldn't be able to withdraw zero amount (for token pool)", async () => { + await protocolDeploymentFixture(); + let fees = await feeSharingCollector.getAccumulatedFees(account1, loanToken.address); + expect(fees).to.be.bignumber.equal("0"); + + await expectRevert( + feeSharingCollector.withdrawTokens([loanToken.address], [10], ZERO_ADDRESS, { + from: account1, + }), + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + }); + + it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => { + await protocolDeploymentFixture(); + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal("0"); + + await expectRevert( + feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ), + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + }); + + it("Should be able to withdraw to another account", async () => { + await protocolDeploymentFixture(); + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "ether")); + let tradingFeeTokensHeld = new BN(wei("2", "ether")); + let borrowingFeeTokensHeld = new BN(wei("3", "ether")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); + + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [1000], + account2, + { + from: account1, + } + ); + + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account2, + token: loanWrappedNativeToken.address, + amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (token pool)", async () => { + await protocolDeploymentFixture(); + // FeeSharingCollector + feeSharingCollector = await FeeSharingCollectorMockup.new( + sovryn.address, + staking.address + ); + await sovryn.setFeesController(feeSharingCollector.address); + + await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // Mock (transfer loanToken to FeeSharingProxy contract) + const loanPoolTokenAddress = await sovryn.underlyingToLoanPool(SUSD.address); + const amountLend = new BN(wei("500", "ether")); + await SUSD.approve(loanPoolTokenAddress, amountLend); + await loanToken.mint(feeSharingCollector.address, amountLend); + + // Check ISUSD Balance for feeSharingProxy + const feeSharingProxyLoanBalanceToken = await loanToken.balanceOf( + feeSharingCollector.address + ); + expect(feeSharingProxyLoanBalanceToken.toString()).to.be.equal(amountLend.toString()); + + // Withdraw ISUSD from feeSharingProxy + // const initial + await feeSharingCollector.addCheckPoint(loanPoolTokenAddress, amountLend.toString()); + let tx = await feeSharingCollector.trueWithdrawTokens( + [loanToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ); + const updatedFeeSharingProxyLoanBalanceToken = await loanToken.balanceOf( + feeSharingCollector.address + ); + const updatedAccount1LoanBalanceToken = await loanToken.balanceOf(account1); + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + + expect(updatedFeeSharingProxyLoanBalanceToken.toString()).to.be.equal( + ((amountLend * 7) / 10).toString() + ); + expect(updatedAccount1LoanBalanceToken.toString()).to.be.equal( + ((amountLend * 3) / 10).toString() + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: loanToken.address, + amount: amountLend.mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (WRBTC pool)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let fees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ); + + /// @dev To anticipate gas consumption it is required to split hardhat + /// behaviour into two different scenarios: coverage and regular testing. + /// On coverage gasPrice = 1, on regular tests gasPrice = 8000000000 + // + // On coverage: + // Fees: 1800000000 + // Balance: 10000000000000000000000 + // Balance: 10000000000001799398877 + // withdraw().gasUsed: 601123 + // txFee: 601123 + // + // On regular test: + // Fees: 1800000000 + // Balance: 10000000000000000000000 + // Balance: 9999996433281800000000 + // withdraw().gasUsed: 445840 + // txFee: 3566720000000000 + let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + let gasPrice; + /// @dev A balance decrease (negative difference) corresponds to regular test case + if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") { + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + } // regular test + else { + gasPrice = new BN(1); + } // coverage + + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + // check balances + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userLoanTokenBalance.toNumber()).to.be.equal(0); + let userExpectedBtcBalance = userInitialBtcBalance.add( + feeAmount.mul(new BN(3)).div(new BN(10)) + ); + expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString()); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: loanWrappedNativeToken.address, + amount: feeAmount.mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (sov pool)", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + false, + true + ); + + await feeSharingCollector.withdrawFees([SOVToken.address]); + + let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + let userInitialISOVBalance = await SOVToken.balanceOf(account1); + let tx = await feeSharingCollector.withdrawTokens( + [SOVToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ); + + //processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + SOVToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + //check balances + let feeSharingProxyBalance = await SOVToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + let userBalance = await SOVToken.balanceOf.call(account1); + expect(userBalance.sub(userInitialISOVBalance).toNumber()).to.be.equal( + (feeAmount * 3) / 10 + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account1, + token: SOVToken.address, + amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)), + }); + }); + + it("Should be able to withdraw (sov pool) to another account", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + //mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld, + false, + true + ); + + await feeSharingCollector.withdrawFees([SOVToken.address]); + + let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address); + expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); + + const receiverBalanceBefore = await SOVToken.balanceOf(account2); + let tx = await feeSharingCollector.withdrawTokens([SOVToken.address], [10], account2, { + from: account1, + }); + + //processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + SOVToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + //check balances + let feeSharingProxyBalance = await SOVToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10); + const receiverBalanceAfter = await SOVToken.balanceOf(account2); + const amountWithdrawn = new BN(feeAmount).mul(new BN(3)).div(new BN(10)); + expect(receiverBalanceAfter.sub(receiverBalanceBefore).toString()).to.be.equal( + amountWithdrawn.toString() + ); + + expectEvent(tx, "UserFeeWithdrawn", { + sender: account1, + receiver: account2, + token: SOVToken.address, + amount: amountWithdrawn, + }); + }); + + it("Should be able to withdraw using 3 checkpoints", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 900; + await stake(rootStake, root); + + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // [FIRST] + // mock data + let lendingFeeTokensHeld = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld = new BN(wei("3", "gwei")); + let totalFeeTokensHeld = lendingFeeTokensHeld + .add(tradingFeeTokensHeld) + .add(borrowingFeeTokensHeld); + let feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld, + tradingFeeTokensHeld, + borrowingFeeTokensHeld + ); + let totalFeeAmount = feeAmount; + await feeSharingCollector.withdrawFees([SUSD.address]); + + let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [1], + ZERO_ADDRESS, + { + from: account1, + } + ); + + /// @dev Same as above gas consumption is different on regular tests than on coverge + let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + let gasPrice; + /// @dev A balance decrease (negative difference) corresponds to regular test case + if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") { + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + } // regular test + else { + gasPrice = new BN(1); + } // coverage + + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(1); + + // check balances + let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal((totalFeeAmount * 9) / 10); + let userBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userBalance.toNumber()).to.be.equal(0); + + expect(userLatestBTCBalance.toString()).to.be.equal( + userInitialBtcBalance.add(totalFeeAmount.mul(new BN(1)).div(new BN(10))).toString() + ); + + // [SECOND] + // mock data + let lendingFeeTokensHeld2 = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld2 = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld2 = new BN(wei("3", "gwei")); + totalFeeTokensHeld = lendingFeeTokensHeld2 + .add(tradingFeeTokensHeld2) + .add(borrowingFeeTokensHeld2); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld2, + tradingFeeTokensHeld2, + borrowingFeeTokensHeld2 + ); + totalFeeAmount = totalFeeAmount.add(feeAmount); + let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount; + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + await feeSharingCollector.withdrawFees([SUSD.address]); + + // [THIRD] + // mock data + let lendingFeeTokensHeld3 = new BN(wei("1", "gwei")); + let tradingFeeTokensHeld3 = new BN(wei("2", "gwei")); + let borrowingFeeTokensHeld3 = new BN(wei("3", "gwei")); + totalFeeTokensHeld = lendingFeeTokensHeld3 + .add(tradingFeeTokensHeld3) + .add(borrowingFeeTokensHeld3); + feeAmount = await setFeeTokensHeld( + lendingFeeTokensHeld3, + tradingFeeTokensHeld3, + borrowingFeeTokensHeld3 + ); + totalFeeAmount = totalFeeAmount.add(feeAmount); + totalLoanTokenWRBTCBalanceShouldBeAccount1 = + totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount); + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + await feeSharingCollector.withdrawFees([SUSD.address]); + + // [SECOND] - [THIRD] + userInitialBtcBalance = new BN(await web3.eth.getBalance(account1)); + tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [2], + ZERO_ADDRESS, + { + from: account1, + } + ); + gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice)); + console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed); + txFee = new BN(tx.receipt.gasUsed).mul(gasPrice); + + userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee)); + + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(3); + + // check balances + feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call( + feeSharingCollector.address + ); + expect(feeSharingProxyBalance.toNumber()).to.be.equal( + parseInt((totalFeeAmount * 9) / 10) + ); + userBalance = await loanWrappedNativeToken.balanceOf.call(account1); + expect(userBalance.toNumber()).to.be.equal(0); + + userLatestBTCBalance = new BN(await web3.eth.getBalance(account1)); + + expect(userLatestBTCBalance.toString()).to.be.equal( + userInitialBtcBalance + .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10))) + .toString() + ); + }); + + it("Should be able to process 10 checkpoints", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + await stake(900, root); + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + await createCheckpoints(10); + + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [1000], + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 10).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(10); + }); + + it("Should be able to process 10 checkpoints and 3 withdrawals", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + await stake(900, root); + let userStake = 100; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + // mock data + await createCheckpoints(10); + + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [5], + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 5).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(5); + + tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [3], + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 3).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(8); + + tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [1000], + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed); + // processedCheckpoints + processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( + account1, + loanWrappedNativeToken.address + ); + expect(processedCheckpoints.toNumber()).to.be.equal(10); + }); + + // // use for gas usage tests + // it("Should be able to process 30 checkpoints", async () => { + // // stake - getPriorTotalVotingPower + // await stake(900, root); + // let userStake = 100; + // if (MOCK_PRIOR_WEIGHTED_STAKE) { + // await staking.MOCK_priorWeightedStake(userStake * 10); + // } + // await SOVToken.transfer(account1, userStake); + // await stake(userStake, account1); + // + // // mock data + // await createCheckpoints(30); + // + // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1}); + // console.log("\nwithdraw(checkpoints = 30).gasUsed: " + tx.receipt.gasUsed); + // // processedCheckpoints + // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address); + // expect(processedCheckpoints.toNumber()).to.be.equal(30); + // }); + // + // // use for gas usage tests + // it("Should be able to process 100 checkpoints", async () => { + // // stake - getPriorTotalVotingPower + // await stake(900, root); + // let userStake = 100; + // if (MOCK_PRIOR_WEIGHTED_STAKE) { + // await staking.MOCK_priorWeightedStake(userStake * 10); + // } + // await SOVToken.transfer(account1, userStake); + // await stake(userStake, account1); + // + // // mock data + // await createCheckpoints(100); + // + // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1}); + // console.log("\nwithdraw(checkpoints = 500).gasUsed: " + tx.receipt.gasUsed); + // // processedCheckpoints + // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address); + // expect(processedCheckpoints.toNumber()).to.be.equal(100); + // }); + // + // // use for gas usage tests + // it("Should be able to withdraw when staking contains a lot of checkpoints", async () => { + // let checkpointCount = 1000; + // await stake(1000, root, checkpointCount); + // let afterBlock = await blockNumber(); + // console.log(afterBlock); + // + // let kickoffTS = await staking.kickoffTS.call(); + // let stakingDate = kickoffTS.add(new BN(MAX_DURATION)); + // + // let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints.call(root, stakingDate); + // let firstCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, 0); + // let lastCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, numUserStakingCheckpoints - 1); + // let block1 = firstCheckpoint.fromBlock.toNumber() + 1; + // let block2 = lastCheckpoint.fromBlock; + // + // console.log("numUserStakingCheckpoints = " + numUserStakingCheckpoints.toString()); + // console.log("first = " + firstCheckpoint.fromBlock.toString()); + // console.log("last = " + lastCheckpoint.fromBlock.toString()); + // + // let tx = await staking.calculatePriorWeightedStake(root, block1, stakingDate); + // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed); + // tx = await staking.calculatePriorWeightedStake(root, block2, stakingDate); + // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed); + // }); + + it("Should be able to withdraw with staking for 78 dates", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // stake - getPriorTotalVotingPower + let rootStake = 700; + await stake(rootStake, root); + + let userStake = 300; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + let kickoffTS = await staking.kickoffTS.call(); + await SOVToken.approve(staking.address, userStake * 1000); + for (let i = 0; i < 77; i++) { + let stakingDate = kickoffTS.add(new BN(TWO_WEEKS * (i + 1))); + await staking.stake(userStake, stakingDate, account1, account1); + } + + // mock data + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + + await feeSharingCollector.withdrawFees([SUSD.address]); + + let tx = await feeSharingCollector.withdrawTokens( + [loanWrappedNativeToken.address], + [10], + ZERO_ADDRESS, + { + from: account1, + } + ); + console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed); + }); + + it("should compute the weighted stake and show gas usage", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + await stake(100, root); + let kickoffTS = await staking.kickoffTS.call(); + let stakingDate = kickoffTS.add(new BN(MAX_DURATION)); + await SOVToken.approve(staking.address, 100); + let result = await staking.stake("100", stakingDate, root, root); + await mineBlock(); + + let tx = await iWeightedStakingModuleMockup.calculatePriorWeightedStake( + root, + result.receipt.blockNumber, + stakingDate + ); + console.log("\ngasUsed: " + tx.receipt.gasUsed); + }); + }); + + describe("withdraw with or considering vesting contracts", () => { + it("getAccumulatedFees should return 0 for vesting contracts", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + let { vestingInstance } = await createVestingContractWithSingleDate( + new BN(MAX_DURATION), + 1000, + root + ); + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + let fees = await feeSharingCollector.getAccumulatedFees( + vestingInstance.address, + loanToken.address + ); + expect(fees).to.be.bignumber.equal("0"); + }); + + it("vesting contract should not be able to withdraw fees", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + let { vestingInstance } = await createVestingContractWithSingleDate( + new BN(MAX_DURATION), + 1000, + root + ); + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + await expectRevert( + vestingInstance.collectDividends(loanToken.address, 5, root), + "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + ); + }); + + it("vested stakes should be deducted from total weighted stake on share distribution", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + // 50% vested 50% voluntary stakes + await createVestingContractWithSingleDate(new BN(MAX_DURATION), 1000, root); + let userStake = 1000; + if (MOCK_PRIOR_WEIGHTED_STAKE) { + await staking.MOCK_priorWeightedStake(userStake * 10); + } + await SOVToken.transfer(account1, userStake); + await stake(userStake, account1); + + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + let tx = await feeSharingCollector.withdrawFees([SUSD.address]); + let feesWithdrawn = tx.logs[1].args.amount; + let userFees = await feeSharingCollector.getAccumulatedFees( + account1, + loanWrappedNativeToken.address + ); + + // 100% of the fees should go to the user -> vesting contract not considered + expect(feesWithdrawn).to.be.bignumber.equal(userFees); + }); + }); + + describe("withdraw wrbtc", async () => { + it("Withdraw wrappedNativeToken from non owner should revert", async () => { + await protocolDeploymentFixture(); + const receiver = accounts[1]; + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + await expectRevert( + feeSharingCollector.withdrawWRBTC(receiver, 0, { from: accounts[1] }), + "unauthorized" + ); + }); + + it("Withdraw 0 wrbtc", async () => { + await protocolDeploymentFixture(); + const receiver = accounts[1]; + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + await feeSharingCollector.withdrawWRBTC(receiver, 0); + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + expect( + new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString() + ).to.equal("0"); + expect(latestBalanceFeeSharingProxy.toString()).to.equal("0"); + }); + + it("Withdraw wrappedNativeToken more than the balance of feeSharingProxy should revert", async () => { + await protocolDeploymentFixture(); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); + + const receiver = accounts[1]; + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingProxyBalance = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + const amount = feeSharingProxyBalance.add(new BN(100)); + const previousBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + await expectRevert( + feeSharingCollector.withdrawWRBTC(receiver, amount.toString()), + "Insufficient balance" + ); + + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + expect( + new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString() + ).to.equal("0"); + expect(latestBalanceFeeSharingProxy.toString()).to.equal( + previousBalanceFeeSharingProxy.toString() + ); + }); + + it("Fully Withdraw wrbtc", async () => { + await protocolDeploymentFixture(); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); + + const receiver = accounts[1]; + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingProxyBalance = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + const tx = await feeSharingCollector.withdrawWRBTC( + receiver, + feeSharingProxyBalance.toString() + ); + await expectEvent.inTransaction( + tx.receipt.rawLogs[0].transactionHash, + WrappedNativeToken, + "Transfer", + { + src: feeSharingCollector.address, + dst: receiver, + wad: feeSharingProxyBalance.toString(), + } + ); + + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + expect( + new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString() + ).to.equal(feeSharingProxyBalance.toString()); + expect(latestBalanceFeeSharingProxy.toString()).to.equal("0"); + }); + + it("Partially Withdraw wrbtc", async () => { + await protocolDeploymentFixture(); + await WrappedNativeToken.mint(root, wei("500", "ether")); + await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether")); + + const receiver = accounts[1]; + const restAmount = new BN("100"); // 100 wei + const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const feeSharingProxyBalance = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + const amount = feeSharingProxyBalance.sub(restAmount); + const previousBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + expect(previousBalanceFeeSharingProxy.toString()).to.equal(wei("1", "ether")); + + const tx = await feeSharingCollector.withdrawWRBTC(receiver, amount.toString()); + await expectEvent.inTransaction( + tx.receipt.rawLogs[0].transactionHash, + WrappedNativeToken, + "Transfer", + { + src: feeSharingCollector.address, + dst: receiver, + wad: amount, + } + ); + + const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + + expect( + new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString() + ).to.equal(amount.toString()); + expect(latestBalanceFeeSharingProxy.toString()).to.equal(restAmount.toString()); + + // try to withdraw the rest + const tx2 = await feeSharingCollector.withdrawWRBTC( + receiver, + latestBalanceFeeSharingProxy.toString() + ); + const finalBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); + const finalBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); + expect(new BN(finalBalanceReceiver).toString()).to.equal( + previousBalanceFeeSharingProxy.toString() + ); + expect(finalBalanceFeeSharingProxy.toString()).to.equal("0"); + + await expectEvent.inTransaction( + tx2.receipt.rawLogs[0].transactionHash, + WrappedNativeToken, + "Transfer", + { + src: feeSharingCollector.address, + dst: receiver, + wad: latestBalanceFeeSharingProxy.toString(), + } + ); + }); + }); + + async function stake(amount, user, checkpointCount) { + await SOVToken.approve(staking.address, amount); + let kickoffTS = await staking.kickoffTS.call(); + let stakingDate = kickoffTS.add(new BN(MAX_DURATION)); + let tx = await staking.stake(amount, stakingDate, user, user); + await mineBlock(); + + if (checkpointCount > 0) { + await increaseStake(amount, user, stakingDate, checkpointCount - 1); + } + + return tx; + } + + async function increaseStake(amount, user, stakingDate, checkpointCount) { + for (let i = 0; i < checkpointCount; i++) { + await SOVToken.approve(staking.address, amount); + await staking.increaseStake(amount, user, stakingDate); + } + } + + async function setFeeTokensHeld( + lendingFee, + tradingFee, + borrowingFee, + wrbtcTokenFee = false, + sovTokenFee = false + ) { + let totalFeeAmount = lendingFee.add(tradingFee).add(borrowingFee); + let tokenFee; + if (wrbtcTokenFee) { + tokenFee = WrappedNativeToken; + } else { + tokenFee = SUSD; + await tokenFee.transfer(sovryn.address, totalFeeAmount); + } + await sovryn.setLendingFeeTokensHeld(tokenFee.address, lendingFee); + await sovryn.setTradingFeeTokensHeld(tokenFee.address, tradingFee); + await sovryn.setBorrowingFeeTokensHeld(tokenFee.address, borrowingFee); + + if (sovTokenFee) { + await SOVToken.transfer(sovryn.address, totalFeeAmount); + await sovryn.setLendingFeeTokensHeld(SOVToken.address, lendingFee); + await sovryn.setTradingFeeTokensHeld(SOVToken.address, tradingFee); + await sovryn.setBorrowingFeeTokensHeld(SOVToken.address, borrowingFee); + } + return totalFeeAmount; + } + + async function checkWithdrawFee(checkSUSD = true, checkWRBTC = false, checkSOV = false) { + if (checkSUSD) { + let protocolBalance = await SUSD.balanceOf(sovryn.address); + expect(protocolBalance.toString()).to.be.equal(new BN(0).toString()); + let lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(SUSD.address); + expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + let tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(SUSD.address); + expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + let borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(SUSD.address); + expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + } + + if (checkWRBTC) { + lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call( + WrappedNativeToken.address + ); + expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call( + WrappedNativeToken.address + ); + expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call( + WrappedNativeToken.address + ); + expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + } + + if (checkSOV) { + protocolBalance = await SOVToken.balanceOf(sovryn.address); + expect(protocolBalance.toString()).to.be.equal(new BN(0).toString()); + lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(SOVToken.address); + expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(SOVToken.address); + expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(SOVToken.address); + expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString()); + } + } + + async function createCheckpoints(number) { + for (let i = 0; i < number; i++) { + await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); + await increaseTime(FEE_WITHDRAWAL_INTERVAL); + await feeSharingCollector.withdrawFees([SUSD.address]); + } + } + + async function createVestingContractWithSingleDate(cliff, amount, tokenOwner) { + vestingLogic = await VestingLogic.new(); + let vestingInstance = await Vesting.new( + vestingLogic.address, + SOVToken.address, + staking.address, + tokenOwner, + cliff, + cliff, + feeSharingCollector.address + ); + vestingInstance = await VestingLogic.at(vestingInstance.address); + // important, so it's recognized as vesting contract + await staking.addContractCodeHash(vestingInstance.address); + + await SOVToken.approve(vestingInstance.address, amount); + let result = await vestingInstance.stakeTokens(amount); + return { vestingInstance: vestingInstance, blockNumber: result.receipt.blockNumber }; + } +}); From d8ef7a68f1751c1f1e9a62dba129dcb7513c03b2 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Thu, 13 Jun 2024 22:37:13 +0700 Subject: [PATCH 06/15] revert old feeSharingCollector --- .../FeeSharingCollector.sol | 104 +---------- tests/FeeSharingCollectorTest.js | 162 +----------------- 2 files changed, 10 insertions(+), 256 deletions(-) diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index 4cd8bda2f..bbd9438e9 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -9,7 +9,6 @@ import "../IFeeSharingCollector.sol"; import "../../openzeppelin/Address.sol"; import "./FeeSharingCollectorStorage.sol"; import "../../interfaces/IConverterAMM.sol"; -import "../../interfaces/ISovrynDex.sol"; /** * @title The FeeSharingCollector contract. @@ -59,8 +58,6 @@ contract FeeSharingCollector is address constant ZERO_ADDRESS = address(0); address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); - uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; - uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; /* Events */ @@ -99,15 +96,6 @@ contract FeeSharingCollector is */ event FeeAMMWithdrawn(address indexed sender, address indexed converter, uint256 amount); - /** - * @notice An event emitted when fee from Dex get withdrawn. - * - * @param sender sender who initiate the withdrawn dex protocol fees. - * @param token the token address. - * @param amount total amount of fee (Already converted to wrappedNativeToken). - */ - event FeeDexWithdrawn(address indexed sender, address indexed token, uint256 amount); - /// @notice An event emitted when converter address has been registered to be whitelisted. event WhitelistedConverter(address indexed sender, address converter); @@ -130,11 +118,15 @@ contract FeeSharingCollector is event SetProtocolAddress(address indexed sender, address _protocolAddress); - event SetSovrynDexAddress( - address indexed sender, - address indexed oldSovrynDexAddress, - address indexed newSovrynDexAddress - ); + /* Modifier */ + modifier oneTimeExecution(bytes4 _funcSig) { + require( + !isFunctionExecuted[_funcSig], + "FeeSharingCollector: function can only be called once" + ); + _; + isFunctionExecuted[_funcSig] = true; + } /* Functions */ @@ -194,14 +186,6 @@ contract FeeSharingCollector is emit SetProtocolAddress(msg.sender, _protocolAddress); } - function setSovrynDexAddress( - address _sovrynDexAddress - ) public onlyOwner oneTimeExecution(this.setSovrynDexAddress.selector) { - require(Address.isContract(_sovrynDexAddress), "_sovrynDexAddress not a contract"); - emit SetSovrynDexAddress(msg.sender, sovrynDexAddress, _sovrynDexAddress); - sovrynDexAddress = _sovrynDexAddress; - } - /** * @notice Set the loan wrappedNativeToken token address of fee sharing collector. * @@ -314,63 +298,6 @@ contract FeeSharingCollector is } } - /** - * @notice Withdraw amm fees for the given converter addresses: - * protocolFee from the conversion - * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool - * - * @param _tokens array addresses of the tokens - * */ - function withdrawFeesFromDex(address[] memory _tokens) public { - for (uint256 i = 0; i < _tokens.length; i++) { - require( - Address.isContract(_tokens[i]), - "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" - ); - } - - IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20( - wrappedNativeTokenAddress - ); - - uint96 totalPoolTokenAmount; - for (uint256 i = 0; i < _tokens.length; i++) { - address token = _tokens[i]; - /** Withdraw from dex */ - bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, token); - bytes memory withdrawnData = ISovrynDex(sovrynDexAddress).userCmd( - SOVRYN_DEX_COLD_PATH_PROXY_IDX, - cmd - ); - - // Convert the return bytes data from dex to uint256 - uint256 wrappedNativeTokenAmountWithdrawn = bytesToUint256(withdrawnData); - - if (wrappedNativeTokenAmountWithdrawn > 0) { - // unwrap wrappedNativeToken to nativeToken, and hold the nativeToken - wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn); - - /// @notice Update unprocessed amount of tokens - uint96 amount96 = safe96( - wrappedNativeTokenAmountWithdrawn, - "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken token amount exceeds 96 bits" - ); - - totalPoolTokenAmount = add96( - totalPoolTokenAmount, - amount96, - "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken token amount exceeds 96 bits" - ); - - emit FeeDexWithdrawn(msg.sender, token, wrappedNativeTokenAmountWithdrawn); - } - } - - if (totalPoolTokenAmount > 0) { - _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); - } - } - /** * @notice Transfer tokens to this contract. * @dev We just update amount of tokens here and write checkpoint in a separate methods @@ -1368,19 +1295,6 @@ contract FeeSharingCollector is function numTokenCheckpoints(address _token) external view returns (uint256) { return totalTokenCheckpoints[_token]; } - - // Function to convert bytes to uint256 - function bytesToUint256(bytes memory b) private pure returns (uint256) { - require(b.length == 32, "Invalid bytes length, must be 32 bytes"); - - uint256 result; - - assembly { - result := mload(add(b, 0x20)) - } - - return result; - } } interface ILoanWrappedNativeToken { diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index 18fdd8d60..c179188bd 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -80,8 +80,6 @@ const SwapsExternal = artifacts.require("SwapsExternal"); const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup"); const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup"); -const MockSovrynDex = artifacts.require("MockSovrynDex"); - const TOTAL_SUPPLY = etherMantissa(1000000000); const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092)); @@ -136,7 +134,6 @@ contract("FeeSharingCollector:", (accounts) => { let mockPrice; let liquidityPoolV1Converter; let iWeightedStakingModuleMockup; - let sovrynDex; before(async () => { [root, account1, account2, account3, account4, ...accounts] = accounts; @@ -331,9 +328,6 @@ contract("FeeSharingCollector:", (accounts) => { loanWrappedNativeToken.address ); - sovrynDex = await MockSovrynDex.new(); - await feeSharingCollector.setSovrynDexAddress(sovrynDex.address); - return sovryn; } @@ -500,15 +494,6 @@ contract("FeeSharingCollector:", (accounts) => { newLoanWrappedNativeTokenAddress ); }); - - it("setSovrynDexAddress should only be called once", async () => { - expect(await feeSharingCollector.sovrynDexAddress()).to.equal(sovrynDex.address); - const newSovrynDexAddress = (await MockSovrynDex.new()).address; - await expectRevert( - feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), - "FeeSharingCollector: function can only be called once" - ); - }); }); describe("withdrawStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => { @@ -5205,151 +5190,6 @@ contract("FeeSharingCollector:", (accounts) => { }); }); - describe("withdraw Dex Fees", async () => { - it("should not be able to withdraw fees if invalid token address", async () => { - /// @dev This test requires redeploying the protocol - await protocolDeploymentFixture(); - - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([accounts[0]]), - "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" - ); - }); - - it("Should be able to withdraw Dex Fees", async () => { - /// @dev This test requires redeploying the protocol - await protocolDeploymentFixture(); - - //stake - getPriorTotalVotingPower - let totalStake = 1000; - await stake(totalStake, root); - - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([SUSD.address]), - "Only Treasury" - ); - - //mock data - const feeAmount = new BN(wei("1", "ether")); - await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); - await sovrynDex.setTreasury(feeSharingCollector.address); - await sovrynDex.setWrbtcToken(WrappedNativeToken.address); - - await WrappedNativeToken.mint(sovrynDex.address, wei("2", "ether")); - - let previousFeeSharingCollectorProxyNativeTokenBalance = new BN( - await web3.eth.getBalance(feeSharingCollector.address) - ); - tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); - - //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0"); - - // nativeToken balance of feeSharingCollector should be increased - let latestFeeSharingCollectorProxyNativeTokenBalance = new BN( - await web3.eth.getBalance(feeSharingCollector.address) - ); - expect( - previousFeeSharingCollectorProxyNativeTokenBalance - .add(new BN(feeAmount)) - .toString() - ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString()); - - // make sure wrappedNativeToken balance is 0 after withdrawal - let feeSharingCollectorProxyWrappedNativeTokenBalance = - await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); - expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( - new BN(0).toString() - ); - - //checkpoints - let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ); - expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); - let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, - 0 - ); - expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); - expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal( - totalStake * MAX_VOTING_WEIGHT - ); - expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); - - //check lastFeeWithdrawalTime - let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ); - let block = await web3.eth.getBlock(tx.receipt.blockNumber); - expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); - - expectEvent(tx, "FeeDexWithdrawn", { - sender: root, - token: SUSD.address, - amount: feeAmount, - }); - }); - - it("Should be able to withdraw with 0 AMM Fees", async () => { - /// @dev This test requires redeploying the protocol - await protocolDeploymentFixture(); - - //stake - getPriorTotalVotingPower - let totalStake = 1000; - await stake(totalStake, root); - - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([SUSD.address]), - "Only Treasury" - ); - - //mock data - const feeAmount = new BN(wei("0", "ether")); - await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); - await sovrynDex.setTreasury(feeSharingCollector.address); - await sovrynDex.setWrbtcToken(WrappedNativeToken.address); - - await WrappedNativeToken.mint(sovrynDex.address, wei("2", "ether")); - - tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); - //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee) - let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call( - feeSharingCollector.address - ); - expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString()); - - // make sure wrappedNativeToken balance is 0 after withdrawal - let feeSharingCollectorProxyWrappedNativeTokenBalance = - await WrappedNativeToken.balanceOf.call(feeSharingCollector.address); - expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal( - new BN(0).toString() - ); - - //checkpoints - let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - loanWrappedNativeToken.address - ); - expect(totalTokenCheckpoints.toNumber()).to.be.equal(0); - let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - loanWrappedNativeToken.address, - 0 - ); - expect(checkpoint.blockNumber.toNumber()).to.be.equal(0); - expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(0); - expect(checkpoint.numTokens.toString()).to.be.equal("0"); - - //check lastFeeWithdrawalTime - let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - loanWrappedNativeToken.address - ); - expect(lastFeeWithdrawalTime.toString()).to.be.equal("0"); - }); - }); - describe("withdraw wrappedNativeToken", async () => { it("Withdraw wrappedNativeToken from non owner should revert", async () => { await protocolDeploymentFixture(); @@ -5991,4 +5831,4 @@ contract("FeeSharingCollector:", (accounts) => { let result = await vestingInstance.stakeTokens(amount); return { vestingInstance: vestingInstance, blockNumber: result.receipt.blockNumber }; } -}); +}); \ No newline at end of file From 7594a8ba220da982a586285db00e165840bdf40d Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Thu, 13 Jun 2024 22:37:36 +0700 Subject: [PATCH 07/15] ran prettier --- tests/FeeSharingCollectorTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index c179188bd..a2146784d 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -5831,4 +5831,4 @@ contract("FeeSharingCollector:", (accounts) => { let result = await vestingInstance.stakeTokens(amount); return { vestingInstance: vestingInstance, blockNumber: result.receipt.blockNumber }; } -}); \ No newline at end of file +}); From 8a5a9802c4dcaa588f222d42725a92e27879a8dd Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Mon, 1 Jul 2024 22:29:53 +0700 Subject: [PATCH 08/15] 1/ address comment review --- .../FeeSharingCollector.sol | 96 ++--- ....sol => FeeSharingCollectorMultiToken.sol} | 110 +++--- ...sol => IFeeSharingCollectorMultiToken.sol} | 2 +- contracts/interfaces/ISovrynDex.sol | 2 +- ...FeeSharingCollectorMultipleTokenMockup.sol | 4 +- .../mockup/MockSovrynDexMultipleToken.sol | 7 +- ... => FeeSharingCollectorMultiToken.test.js} | 144 +++++-- tests/FeeSharingCollectorTest.js | 368 +++++++----------- 8 files changed, 346 insertions(+), 387 deletions(-) rename contracts/governance/FeeSharingCollector/{FeeSharingCollectorMultipleToken.sol => FeeSharingCollectorMultiToken.sol} (84%) rename contracts/governance/{IFeeSharingCollectorMultipleToken.sol => IFeeSharingCollectorMultiToken.sol} (92%) rename tests/{FeeSharingCollectorMultipleToken.test.js => FeeSharingCollectorMultiToken.test.js} (96%) diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index bbd9438e9..5c5d2ba27 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -56,8 +56,9 @@ contract FeeSharingCollector is using SafeERC20 for IERC20; address constant ZERO_ADDRESS = address(0); - address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = - address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); + /** To support backward compatibility, we need to keep this constant variable name as it is (which is derived from rsk network) */ + address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT = + address(uint160(uint256(keccak256("RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT")))); /* Events */ @@ -118,16 +119,6 @@ contract FeeSharingCollector is event SetProtocolAddress(address indexed sender, address _protocolAddress); - /* Modifier */ - modifier oneTimeExecution(bytes4 _funcSig) { - require( - !isFunctionExecuted[_funcSig], - "FeeSharingCollector: function can only be called once" - ); - _; - isFunctionExecuted[_funcSig] = true; - } - /* Functions */ /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken. @@ -238,11 +229,11 @@ contract FeeSharingCollector is "FeeSharingCollector::withdrawFees: wrappedNativeToken token amount exceeds 96 bits" ); - _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96); + _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96); } // note deprecated event since we unify the wrappedNativeToken & nativeToken - // emit FeeWithdrawn(msg.sender, NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount); + // emit FeeWithdrawn(msg.sender, RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount); // note new emitted event emit FeeWithdrawnInNativeToken(msg.sender, wrappedNativeTokenAmountWithdrawn); @@ -294,7 +285,7 @@ contract FeeSharingCollector is } if (totalPoolTokenAmount > 0) { - _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); + _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount); } } @@ -319,7 +310,7 @@ contract FeeSharingCollector is ); if (_token == address(wrappedNativeToken)) { wrappedNativeToken.withdraw(_amount); - _token = NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; + _token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT; } _addCheckpoint(_token, _amount); @@ -336,7 +327,7 @@ contract FeeSharingCollector is uint96 _amount = uint96(msg.value); require(_amount > 0, "FeeSharingCollector::transferNativeToken: invalid value"); - _addCheckpoint(NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount); + _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount); emit TokensTransferred(msg.sender, ZERO_ADDRESS, _amount); } @@ -403,7 +394,7 @@ contract FeeSharingCollector is processedCheckpoints[user][_token] = end; if (loanWrappedNativeTokenAddress == _token) { - // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function + // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function which is burning to a native token - to be renamed to burnedToNativeToken ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false); } else { // Previously it directly send the loanWrappedNativeToken to the user @@ -491,11 +482,11 @@ contract FeeSharingCollector is for (uint256 i = 0; i < _tokens.length; i++) { address _token = _tokens[i]; if ( - _token != NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT && + _token != RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT && _token != wrappedNativeTokenAddress && _token != loanWrappedNativeTokenAddress ) { - revert("only nativeToken-based tokens are allowed"); + revert("only native token based tokens are allowed"); } } } @@ -549,7 +540,7 @@ contract FeeSharingCollector is if ( tokenData.tokenAddress == wrappedNativeTokenAddress || tokenData.tokenAddress == loanWrappedNativeTokenAddress || - tokenData.tokenAddress == NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + tokenData.tokenAddress == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ) { (totalAmount, endToken) = _withdrawNativeTokenStartingFromCheckpoint( tokenData.tokenAddress, @@ -576,9 +567,9 @@ contract FeeSharingCollector is } if (nativeTokenAmountToSend > 0) { - // send all nativeToken withdrawal + // send all native token withdrawal (bool success, ) = _receiver.call.value(nativeTokenAmountToSend)(""); - require(success, "FeeSharingCollector::withdra: Withdrawal failed"); + require(success, "FeeSharingCollector: Withdrawal failed"); emit NativeTokenWithdrawn(msg.sender, _receiver, nativeTokenAmountToSend); } @@ -586,12 +577,12 @@ contract FeeSharingCollector is /** * @dev Function to wrap: - * 1. regular withdrawal for both nativeToken & non-nativeToken token - * 2. skipped checkpoints withdrawal for both nativeToken & non-nativeToken token + * 1. regular withdrawal for both nativeToken & non native token token + * 2. skipped checkpoints withdrawal for both native token & non native token * - * @param _nonNativeTokensRegularWithdraw array of non-nativeToken token address with no skipped checkpoints that will be withdrawn + * @param _nonNativeTokensRegularWithdraw array of non native token token address with no skipped checkpoints that will be withdrawn * @param _nativeTokensRegularWithdraw array of nativeToken token address with no skipped checkpoints that will be withdrawn - * @param _tokensWithSkippedCheckpoints array of nativeToken & non-nativeToken TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn + * @param _tokensWithSkippedCheckpoints array of nativeToken & non native token TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn * */ function claimAllCollectedFees( @@ -616,7 +607,7 @@ contract FeeSharingCollector is ); } - /** Process normal non-nativeToken token withdrawal */ + /** Process normal non native token token withdrawal */ for (uint256 i = 0; i < _nonNativeTokensRegularWithdraw.length; i++) { if (_maxCheckpoints == 0) break; uint256 endTokenCheckpoint; @@ -706,7 +697,7 @@ contract FeeSharingCollector is * - iWrappedNativeToken balance which will be unwrapped to nativeToken or * * - * @param _tokens array of either NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address + * @param _tokens array of either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address * @param _maxCheckpoints Maximum number of checkpoints to be processed to workaround block gas limit * @param _receiver An optional tokens receiver (msg.sender used if 0) */ @@ -1134,45 +1125,12 @@ contract FeeSharingCollector is IERC20 wrappedNativeToken = IERC20(wrappedNativeTokenAddress); uint256 balance = wrappedNativeToken.balanceOf(address(this)); - require(wrappedNativeTokenAmount <= balance, "Insufficient balance"); - - wrappedNativeToken.safeTransfer(receiver, wrappedNativeTokenAmount); - } - - /** - * @dev This function is dedicated to recover the wrong fee allocation for the 4 year vesting contracts. - * This function can only be called once - * The affected tokens to be withdrawn - * 1. NativeToken - * 2. ZUSD - * 3. SOV - * The amount for all of the tokens above is hardcoded - * The withdrawn tokens will be sent to the owner. - */ - function recoverIncorrectAllocatedFees() - external - oneTimeExecution(this.recoverIncorrectAllocatedFees.selector) - onlyOwner - { - uint256 nativeTokenAmount = 878778886164898400; - uint256 zusdAmount = 16658600400155126000000; - uint256 sovAmount = 6275898259771202000000; - - address zusdToken = 0xdB107FA69E33f05180a4C2cE9c2E7CB481645C2d; - address sovToken = 0xEFc78fc7d48b64958315949279Ba181c2114ABBd; - - // Withdraw nativeToken - (bool success, ) = owner().call.value(nativeTokenAmount)(""); require( - success, - "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal nativeToken failed" + wrappedNativeTokenAmount <= balance, + "FeeSharingCollector::withdrawWrappedNativeToken:Insufficient balance" ); - // Withdraw ZUSD - IERC20(zusdToken).safeTransfer(owner(), zusdAmount); - - // Withdraw SOV - IERC20(sovToken).safeTransfer(owner(), sovAmount); + wrappedNativeToken.safeTransfer(receiver, wrappedNativeTokenAmount); } /** @@ -1231,7 +1189,7 @@ contract FeeSharingCollector is { (_nativeTokenAmount, _endNativeToken) = _getAccumulatedFees({ _user: _user, - _token: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + _token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, _startFrom: 0, _maxCheckpoints: _maxCheckpoints }); @@ -1253,7 +1211,7 @@ contract FeeSharingCollector is /** * @dev private function that responsible to calculate the user's token that has NativeToken as underlying token (nativeToken, wrappedNativeToken, iWrappedNativeToken) * - * @param _token either NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address + * @param _token either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address * @param _user address of the user. * @param _maxCheckpoints maximum checkpoints. * @@ -1266,7 +1224,7 @@ contract FeeSharingCollector is uint32 _maxCheckpoints ) internal view returns (uint256 _tokenAmount, uint256 _endToken) { if ( - _token == NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT || + _token == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT || _token == wrappedNativeTokenAddress || _token == loanWrappedNativeTokenAddress ) { @@ -1278,7 +1236,7 @@ contract FeeSharingCollector is }); } else { revert( - "FeeSharingCollector::_getNativeTokenBalance: only nativeToken-based tokens are allowed" + "FeeSharingCollector::_getNativeTokenBalance: only native token based tokens are allowed" ); } } diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol similarity index 84% rename from contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol rename to contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol index 8c789df3e..7b1ea3731 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol @@ -5,13 +5,13 @@ import "../Staking/SafeMath96.sol"; import "../../openzeppelin/SafeMath.sol"; import "../../openzeppelin/SafeERC20.sol"; import "../../openzeppelin/Ownable.sol"; -import "../IFeeSharingCollectorMultipleToken.sol"; +import "../IFeeSharingCollectorMultiToken.sol"; import "../../openzeppelin/Address.sol"; import "./FeeSharingCollectorStorage.sol"; import "../../interfaces/ISovrynDex.sol"; /** - * @title The FeeSharingCollectorMultipleToken contract. + * @title The FeeSharingCollectorMultiToken contract. * @notice Staking is not only granting voting rights, but also access to fee * sharing according to the own voting power in relation to the total. Whenever * somebody decides to collect the fees from the protocol, they get transferred @@ -40,12 +40,12 @@ import "../../interfaces/ISovrynDex.sol"; * The protocol is collecting fees in all sorts of currencies and then automatically * supplies them to the respective lending pools. Therefore, all fees are * generating interest for the SOV holders. If one of them withdraws fees, it will - * get pool tokens. It is planned to add the option to convert anything to rBTC + * get pool tokens. It is planned to add the option to convert anything to native token * before withdrawing, but not yet implemented. * */ -contract FeeSharingCollectorMultipleToken is +contract FeeSharingCollectorMultiToken is SafeMath96, - IFeeSharingCollectorMultipleToken, + IFeeSharingCollectorMultiToken, Ownable, FeeSharingCollectorStorage { @@ -53,8 +53,9 @@ contract FeeSharingCollectorMultipleToken is using SafeERC20 for IERC20; address constant ZERO_ADDRESS = address(0); - address public constant NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = - address(uint160(uint256(keccak256("NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT")))); + /** To support backward compatibility, we need to keep this constant variable name as it is (which is derived from rsk network) */ + address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT = + address(uint160(uint256(keccak256("RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT")))); uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; @@ -115,9 +116,7 @@ contract FeeSharingCollectorMultipleToken is * * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken token. * */ - function setWrappedNativeToken( - address newWrappedNativeTokenAddress - ) public onlyOwner oneTimeExecution(this.setWrappedNativeToken.selector) { + function setWrappedNativeToken(address newWrappedNativeTokenAddress) public onlyOwner { require( Address.isContract(newWrappedNativeTokenAddress), "newWrappedNativeTokenAddress not a contract" @@ -146,9 +145,7 @@ contract FeeSharingCollectorMultipleToken is emit SetProtocolAddress(msg.sender, _protocolAddress); } - function setSovrynDexAddress( - address _sovrynDexAddress - ) public onlyOwner oneTimeExecution(this.setSovrynDexAddress.selector) { + function setSovrynDexAddress(address _sovrynDexAddress) public onlyOwner { require(Address.isContract(_sovrynDexAddress), "_sovrynDexAddress not a contract"); emit SetSovrynDexAddress(msg.sender, sovrynDexAddress, _sovrynDexAddress); sovrynDexAddress = _sovrynDexAddress; @@ -157,8 +154,6 @@ contract FeeSharingCollectorMultipleToken is /** * @notice Withdraw fees for the given token: * lendingFee + tradingFee + borrowingFee - * the fees (except SOV) will be converted in wRBTC form, and then will be transferred to wRBTC loan pool. - * For SOV, it will be directly deposited into the FeeSharingCollectorMultipleToken from the protocol. * * @param _tokens array address of the token * */ @@ -166,34 +161,39 @@ contract FeeSharingCollectorMultipleToken is for (uint256 i = 0; i < _tokens.length; i++) { require( Address.isContract(_tokens[i]), - "FeeSharingCollectorMultipleToken::withdrawFees: token is not a contract" + "FeeSharingCollectorMultiToken::withdrawFees: token is not a contract" ); } - uint256 wrbtcAmountWithdrawn = protocol.withdrawFees(_tokens, address(this)); + uint256 wrappedNativeTokenAmountWithdrawn = protocol.withdrawFees(_tokens, address(this)); uint256 poolTokenAmount; - address wRBTCAddress = wrappedNativeTokenAddress; require( - wRBTCAddress != address(0), - "FeeSharingCollectorMultipleToken::withdrawFees: wRBTCAddress is not set" + wrappedNativeTokenAddress != address(0), + "FeeSharingCollectorMultiToken::withdrawFees: wrappedNativeTokenAddress is not set" ); - address loanPoolToken = protocol.underlyingToLoanPool(wRBTCAddress); + address loanPoolToken = protocol.underlyingToLoanPool(wrappedNativeTokenAddress); require( loanPoolToken != address(0), - "FeeSharingCollectorMultipleToken::withdrawFees: loan wRBTC not found" + "FeeSharingCollectorMultiToken::withdrawFees: loan wrappedNativeTokenAddress not found" ); - if (wrbtcAmountWithdrawn > 0) { + if (wrappedNativeTokenAmountWithdrawn > 0) { /// @dev TODO can be also used - function addLiquidity(IERC20Token _reserveToken, uint256 _amount, uint256 _minReturn) - IERC20(wRBTCAddress).approve(loanPoolToken, wrbtcAmountWithdrawn); - poolTokenAmount = ILoanToken(loanPoolToken).mint(address(this), wrbtcAmountWithdrawn); + IERC20(wrappedNativeTokenAddress).approve( + loanPoolToken, + wrappedNativeTokenAmountWithdrawn + ); + poolTokenAmount = ILoanToken(loanPoolToken).mint( + address(this), + wrappedNativeTokenAmountWithdrawn + ); /// @notice Update unprocessed amount of tokens uint96 amount96 = safe96( poolTokenAmount, - "FeeSharingCollectorMultipleToken::withdrawFees: pool token amount exceeds 96 bits" + "FeeSharingCollectorMultiToken::withdrawFees: pool token amount exceeds 96 bits" ); _addCheckpoint(loanPoolToken, amount96); @@ -203,7 +203,7 @@ contract FeeSharingCollectorMultipleToken is } /** - * @notice Withdraw fees from sovryn dex: + * @notice Withdraw fees from Sovryn DEX * protocolFee from the conversion * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool * @@ -229,15 +229,16 @@ contract FeeSharingCollectorMultipleToken is * @notice Transfer tokens to this contract. * @dev We just update amount of tokens here and write checkpoint in a separate methods * in order to prevent adding checkpoints too often. + * @dev the caller should take care of setting allowance for the token * @param _token Address of the token. * @param _amount Amount to be transferred. * */ function transferTokens(address _token, uint96 _amount) public { require( _token != ZERO_ADDRESS, - "FeeSharingCollectorMultipleToken::transferTokens: invalid address" + "FeeSharingCollectorMultiToken::transferTokens: invalid address" ); - require(_amount > 0, "FeeSharingCollectorMultipleToken::transferTokens: invalid amount"); + require(_amount > 0, "FeeSharingCollectorMultiToken::transferTokens: invalid amount"); /// @notice Transfer tokens from msg.sender bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount); @@ -249,7 +250,7 @@ contract FeeSharingCollectorMultipleToken is ); if (_token == address(wrappedNativeToken)) { wrappedNativeToken.withdraw(_amount); - _token = NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; + _token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT; } _addCheckpoint(_token, _amount); @@ -267,7 +268,7 @@ contract FeeSharingCollectorMultipleToken is uint96 amount = add96( unprocessedAmount[_token], _amount, - "FeeSharingCollectorMultipleToken::_addCheckpoint: amount exceeds 96 bits" + "FeeSharingCollectorMultiToken::_addCheckpoint: amount exceeds 96 bits" ); /// @notice Reset unprocessed amount of tokens to zero. @@ -279,7 +280,7 @@ contract FeeSharingCollectorMultipleToken is unprocessedAmount[_token] = add96( unprocessedAmount[_token], _amount, - "FeeSharingCollectorMultipleToken::_addCheckpoint: unprocessedAmount exceeds 96 bits" + "FeeSharingCollectorMultiToken::_addCheckpoint: unprocessedAmount exceeds 96 bits" ); } } @@ -359,19 +360,19 @@ contract FeeSharingCollectorMultipleToken is /// @dev Prevents processing / checkpoints because of block gas limit. require( _maxCheckpoint > 0, - "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive" ); address _wrappedNativeTokenAddress = wrappedNativeTokenAddress; require( _wrappedNativeTokenAddress != address(0), - "FeeSharingCollectorMultipleToken::withdraw: _wrappedNativeTokenAddress is not set" + "FeeSharingCollectorMultiToken::withdraw: _wrappedNativeTokenAddress is not set" ); address loanWrappedNativeToken = protocol.underlyingToLoanPool(_wrappedNativeTokenAddress); require( loanWrappedNativeToken != address(0), - "FeeSharingCollectorMultipleToken::withdraw: loan wrapped native token not found" + "FeeSharingCollectorMultiToken::withdraw: loan wrapped native token not found" ); address user = msg.sender; @@ -384,23 +385,18 @@ contract FeeSharingCollectorMultipleToken is (amount, end) = _getAccumulatedFees(user, _token, _maxCheckpoint); require( amount > 0, - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); processedCheckpoints[user][_token] = end; if (loanWrappedNativeToken == _token) { - // We will change, so that FeeSharingCollectorMultipleToken will directly burn then loanToken (IWRBTC) to rbtc and send to the user --- by call burnToBTC function - uint256 loanAmountPaid = ILoanWrappedNativeToken(_token).burnToBTC( - _receiver, - amount, - false - ); + // We will change, so that FeeSharingCollectorMultiToken will directly burn then loan wrapped native token to native token and send to the user --- by call burnToBTC function + ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false); } else { - // Previously it directly send the token to the user require( IERC20(_token).transfer(_receiver, amount), - "FeeSharingCollectorMultipleToken::withdraw: withdrawal failed" + "FeeSharingCollectorMultiToken::withdraw: withdrawal failed" ); } @@ -457,7 +453,7 @@ contract FeeSharingCollectorMultipleToken is /// @dev withdraw -> _getAccumulatedFees require( start < totalTokenCheckpoints[_loanPoolToken], - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); end = _getEndOfRange(start, _loanPoolToken, _maxCheckpoints); } else { @@ -522,7 +518,7 @@ contract FeeSharingCollectorMultipleToken is } end = safe32( start + _maxCheckpoints, - "FeeSharingCollectorMultipleToken::withdraw: checkpoint index exceeds 32 bits" + "FeeSharingCollectorMultiToken::withdraw: checkpoint index exceeds 32 bits" ); if (end > nCheckpoints) { end = nCheckpoints; @@ -546,11 +542,11 @@ contract FeeSharingCollectorMultipleToken is function _writeTokenCheckpoint(address _token, uint96 _numTokens) internal { uint32 blockNumber = safe32( block.number, - "FeeSharingCollectorMultipleToken::_writeCheckpoint: block number exceeds 32 bits" + "FeeSharingCollectorMultiToken::_writeCheckpoint: block number exceeds 32 bits" ); uint32 blockTimestamp = safe32( block.timestamp, - "FeeSharingCollectorMultipleToken::_writeCheckpoint: block timestamp exceeds 32 bits" + "FeeSharingCollectorMultiToken::_writeCheckpoint: block timestamp exceeds 32 bits" ); uint256 nCheckpoints = totalTokenCheckpoints[_token]; @@ -588,21 +584,23 @@ contract FeeSharingCollectorMultipleToken is totalWeightedStake = sub96( totalWeightedStake, vestingWeightedStake, - "FeeSharingCollectorMultipleToken::_getTotalVoluntaryWeightedStake: vested stake exceeds total stake" + "FeeSharingCollectorMultiToken::_getTotalVoluntaryWeightedStake: vested stake exceeds total stake" ); } - function withdrawWRBTC(address receiver, uint256 wrbtcAmount) external onlyOwner { - address wRBTCAddress = wrappedNativeTokenAddress; + function withdrawWrappedNativeToken( + address receiver, + uint256 wrappedNativeTokenAmount + ) external onlyOwner { require( - wRBTCAddress != address(0), - "FeeSharingCollectorMultipleToken::withdrawFees: wRBTCAddress is not set" + wrappedNativeTokenAddress != address(0), + "FeeSharingCollectorMultiToken::withdrawFees: wrappedNativeTokenAddress is not set" ); - uint256 balance = IERC20(wRBTCAddress).balanceOf(address(this)); - require(wrbtcAmount <= balance, "Insufficient balance"); + uint256 balance = IERC20(wrappedNativeTokenAddress).balanceOf(address(this)); + require(wrappedNativeTokenAmount <= balance, "Insufficient balance"); - IERC20(wRBTCAddress).safeTransfer(receiver, wrbtcAmount); + IERC20(wrappedNativeTokenAddress).safeTransfer(receiver, wrappedNativeTokenAmount); } } diff --git a/contracts/governance/IFeeSharingCollectorMultipleToken.sol b/contracts/governance/IFeeSharingCollectorMultiToken.sol similarity index 92% rename from contracts/governance/IFeeSharingCollectorMultipleToken.sol rename to contracts/governance/IFeeSharingCollectorMultiToken.sol index 473c2ffeb..54277b470 100644 --- a/contracts/governance/IFeeSharingCollectorMultipleToken.sol +++ b/contracts/governance/IFeeSharingCollectorMultiToken.sol @@ -4,7 +4,7 @@ pragma solidity ^0.5.17; * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol * @dev Interfaces are used to cast a contract address into a callable instance. * */ -interface IFeeSharingCollectorMultipleToken { +interface IFeeSharingCollectorMultiToken { function withdrawFees(address[] calldata _token) external; function transferTokens(address _token, uint96 _amount) external; diff --git a/contracts/interfaces/ISovrynDex.sol b/contracts/interfaces/ISovrynDex.sol index 6b08beede..4ea0b63b2 100644 --- a/contracts/interfaces/ISovrynDex.sol +++ b/contracts/interfaces/ISovrynDex.sol @@ -1,5 +1,5 @@ pragma solidity 0.5.17; interface ISovrynDex { - function userCmd(uint16 callpath, bytes calldata cmd) external payable returns (bytes memory); + function userCmd(uint16 callpath, bytes calldata cmd) external payable; } diff --git a/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol index 97a125174..c0f379db3 100644 --- a/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol +++ b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol @@ -1,8 +1,8 @@ pragma solidity ^0.5.17; -import "../governance/FeeSharingCollector/FeeSharingCollectorMultipleToken.sol"; +import "../governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol"; -contract FeeSharingCollectorMultipleTokenMockup is FeeSharingCollectorMultipleToken { +contract FeeSharingCollectorMultiTokenMockup is FeeSharingCollectorMultiToken { struct TestData { address loanPoolToken; uint32 maxCheckpoints; diff --git a/contracts/mockup/MockSovrynDexMultipleToken.sol b/contracts/mockup/MockSovrynDexMultipleToken.sol index 378be1dc4..55c5bb8c7 100644 --- a/contracts/mockup/MockSovrynDexMultipleToken.sol +++ b/contracts/mockup/MockSovrynDexMultipleToken.sol @@ -1,9 +1,9 @@ pragma solidity 0.5.17; import "../interfaces/IERC20.sol"; -import "../governance/IFeeSharingCollectorMultipleToken.sol"; +import "../governance/IFeeSharingCollectorMultiToken.sol"; -contract MockSovrynDexMultipleToken { +contract MockSovrynDexMultiToken { mapping(address => uint96) public tokenFees; uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; @@ -27,7 +27,8 @@ contract MockSovrynDexMultipleToken { callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE ) { - IFeeSharingCollectorMultipleToken(treasury).transferTokens(token, tokenFees[token]); + IERC20(token).approve(treasury, tokenFees[token]); + IFeeSharingCollectorMultiToken(treasury).transferTokens(token, tokenFees[token]); } } diff --git a/tests/FeeSharingCollectorMultipleToken.test.js b/tests/FeeSharingCollectorMultiToken.test.js similarity index 96% rename from tests/FeeSharingCollectorMultipleToken.test.js rename to tests/FeeSharingCollectorMultiToken.test.js index ee547e1bf..84ddc6a1b 100644 --- a/tests/FeeSharingCollectorMultipleToken.test.js +++ b/tests/FeeSharingCollectorMultiToken.test.js @@ -4,14 +4,14 @@ const { expectRevert, expectEvent, constants, BN } = require("@openzeppelin/test const { ZERO_ADDRESS } = constants; -const { etherMantissa, mineBlock, increaseTime } = require("./Utils/Ethereum"); +const { etherMantissa, mineBlock, increaseTime } = require("./Utils/Ethereum.js"); const { deployAndGetIStaking, replaceStakingModule, getStakingModulesObject, getStakingModulesAddressList, -} = require("./Utils/initializer"); +} = require("./Utils/initializer.js"); const TestToken = artifacts.require("TestToken"); @@ -36,10 +36,10 @@ const LoanTokenLogicWrbtc = artifacts.require("LoanTokenLogicWrbtc"); const LoanToken = artifacts.require("LoanToken"); const LockedSOV = artifacts.require("LockedSOV"); -const FeeSharingCollector = artifacts.require("FeeSharingCollectorMultipleToken"); +const FeeSharingCollector = artifacts.require("FeeSharingCollectorMultiToken"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); -const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMultipleTokenMockup"); -const MockSovrynDex = artifacts.require("MockSovrynDexMultipleToken"); +const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMultiTokenMockup"); +const MockSovrynDex = artifacts.require("MockSovrynDexMultiToken"); const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup"); const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup"); @@ -66,9 +66,9 @@ const MOCK_PRIOR_WEIGHTED_STAKE = false; const wei = web3.utils.toWei; -const { lend_btc_before_cashout } = require("./loan-token/helpers"); +const { lend_btc_before_cashout } = require("./loan-token/helpers.js"); -const mutexUtils = require("../deployment/helpers/reentrancy/utils"); +const mutexUtils = require("../deployment/helpers/reentrancy/utils.js"); let cliff = 1; // This is in 4 weeks. i.e. 1 * 4 weeks. let duration = 11; // This is in 4 weeks. i.e. 11 * 4 weeks. @@ -90,7 +90,7 @@ const { getSOV, } = require("./Utils/initializer.js"); -contract("FeeSharingCollectorMultipleToken:", (accounts) => { +contract("FeeSharingCollectorMultiToken:", (accounts) => { const name = "Test SOVToken"; const symbol = "TST"; @@ -335,15 +335,6 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { "function can only be called once" ); }); - - it("setSovrynDexAddress should only be called once", async () => { - expect(await feeSharingCollector.sovrynDexAddress()).to.equal(sovrynDex.address); - const newSovrynDexAddress = (await MockSovrynDex.new()).address; - await expectRevert( - feeSharingCollector.setSovrynDexAddress(newSovrynDexAddress), - "FeeSharingCollector: function can only be called once" - ); - }); }); describe("FeeSharingCollectorProxy", () => { @@ -379,7 +370,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await protocolDeploymentFixture(); await expectRevert( feeSharingCollector.withdrawFees([ZERO_ADDRESS]), - "FeeSharingCollectorMultipleToken::withdrawFees: token is not a contract" + "FeeSharingCollectorMultiToken::withdrawFees: token is not a contract" ); }); @@ -404,7 +395,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await expectRevert( feeSharingCollector.withdrawFees([WrappedNativeToken.address]), - "FeeSharingCollectorMultipleToken::withdrawFees: loan wRBTC not found" + "FeeSharingCollectorMultiToken::withdrawFees: loan wrappedNativeTokenAddress not found" ); }); @@ -908,12 +899,90 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { }); }); + describe("withdraw Dex Fees", async () => { + it("should not be able to withdraw fees if invalid token address", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([accounts[0]]), + "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" + ); + }); + + it("Should be able to withdraw Dex Fees", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([SUSD.address]), + "Only Treasury" + ); + + //mock data + const feeAmount = new BN(wei("1", "ether")); + await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); + await sovrynDex.setTreasury(feeSharingCollector.address); + await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + + await SUSD.mint(sovrynDex.address, wei("2000", "ether")); + + let previousProtocolBalance = await SUSD.balanceOf(sovrynDex.address); + let previousFeeSharingCollectorBalance = await SUSD.balanceOf( + feeSharingCollector.address + ); + tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); + + let latestProtocolBalance = await SUSD.balanceOf(sovrynDex.address); + let latestFeeSharingCollectorBalance = await SUSD.balanceOf( + feeSharingCollector.address + ); + + expect(previousProtocolBalance.toString()).to.equal( + latestProtocolBalance.add(feeAmount).toString() + ); + + expect(previousFeeSharingCollectorBalance.toString()).to.equal("0"); + expect(latestFeeSharingCollectorBalance.toString()).to.equal(feeAmount.toString()); + }); + + it("Should not be able to withdraw with 0 AMM Fees", async () => { + /// @dev This test requires redeploying the protocol + await protocolDeploymentFixture(); + + //stake - getPriorTotalVotingPower + let totalStake = 1000; + await stake(totalStake, root); + + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([SUSD.address]), + "Only Treasury" + ); + + //mock data + const feeAmount = new BN(wei("0", "ether")); + await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); + await sovrynDex.setTreasury(feeSharingCollector.address); + await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + + await SUSD.mint(sovrynDex.address, wei("2", "ether")); + await expectRevert( + feeSharingCollector.withdrawFeesFromDex([SUSD.address]), + "FeeSharingCollectorMultiToken::transferTokens: invalid amount" + ); + }); + }); + describe("transferTokens", () => { it("Shouldn't be able to use zero token address", async () => { await protocolDeploymentFixture(); await expectRevert( feeSharingCollector.transferTokens(ZERO_ADDRESS, 1000), - "FeeSharingCollectorMultipleToken::transferTokens: invalid address" + "FeeSharingCollectorMultiToken::transferTokens: invalid address" ); }); @@ -921,7 +990,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await protocolDeploymentFixture(); await expectRevert( feeSharingCollector.transferTokens(SOVToken.address, 0), - "FeeSharingCollectorMultipleToken::transferTokens: invalid amount" + "FeeSharingCollectorMultiToken::transferTokens: invalid amount" ); }); @@ -1026,7 +1095,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await protocolDeploymentFixture(); await expectRevert( feeSharingCollector.withdraw(loanToken.address, 0, account2, { from: account1 }), - "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive" ); }); @@ -1036,7 +1105,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.withdraw(loanWrappedNativeToken.address, 0, account2, { from: account1, }), - "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive" ); }); @@ -1049,7 +1118,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.withdraw(loanToken.address, 10, ZERO_ADDRESS, { from: account1, }), - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); }); @@ -1065,7 +1134,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.withdraw(loanWrappedNativeToken.address, 10, ZERO_ADDRESS, { from: account1, }), - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); }); @@ -1813,7 +1882,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.withdrawTokens([loanToken.address], [0], account2, { from: account1, }), - "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive" ); }); @@ -1826,7 +1895,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { account2, { from: account1 } ), - "FeeSharingCollectorMultipleToken::withdraw: _maxCheckpoints should be positive" + "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive" ); }); @@ -1839,7 +1908,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.withdrawTokens([loanToken.address], [10], ZERO_ADDRESS, { from: account1, }), - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); }); @@ -1860,7 +1929,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { from: account1, } ), - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); }); @@ -2636,7 +2705,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await setFeeTokensHeld(new BN(100), new BN(200), new BN(300)); await expectRevert( vestingInstance.collectDividends(loanToken.address, 5, root), - "FeeSharingCollectorMultipleToken::withdrawFees: no tokens for a withdrawal" + "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal" ); }); @@ -2672,7 +2741,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { const receiver = accounts[1]; const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); await expectRevert( - feeSharingCollector.withdrawWRBTC(receiver, 0, { from: accounts[1] }), + feeSharingCollector.withdrawWrappedNativeToken(receiver, 0, { from: accounts[1] }), "unauthorized" ); }); @@ -2681,7 +2750,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { await protocolDeploymentFixture(); const receiver = accounts[1]; const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); - await feeSharingCollector.withdrawWRBTC(receiver, 0); + await feeSharingCollector.withdrawWrappedNativeToken(receiver, 0); const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver); const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf( feeSharingCollector.address @@ -2709,7 +2778,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { ); await expectRevert( - feeSharingCollector.withdrawWRBTC(receiver, amount.toString()), + feeSharingCollector.withdrawWrappedNativeToken(receiver, amount.toString()), "Insufficient balance" ); @@ -2737,7 +2806,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { feeSharingCollector.address ); - const tx = await feeSharingCollector.withdrawWRBTC( + const tx = await feeSharingCollector.withdrawWrappedNativeToken( receiver, feeSharingProxyBalance.toString() ); @@ -2780,7 +2849,10 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { ); expect(previousBalanceFeeSharingProxy.toString()).to.equal(wei("1", "ether")); - const tx = await feeSharingCollector.withdrawWRBTC(receiver, amount.toString()); + const tx = await feeSharingCollector.withdrawWrappedNativeToken( + receiver, + amount.toString() + ); await expectEvent.inTransaction( tx.receipt.rawLogs[0].transactionHash, WrappedNativeToken, @@ -2803,7 +2875,7 @@ contract("FeeSharingCollectorMultipleToken:", (accounts) => { expect(latestBalanceFeeSharingProxy.toString()).to.equal(restAmount.toString()); // try to withdraw the rest - const tx2 = await feeSharingCollector.withdrawWRBTC( + const tx2 = await feeSharingCollector.withdrawWrappedNativeToken( receiver, latestBalanceFeeSharingProxy.toString() ); diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index a2146784d..7eed1bce3 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -122,7 +122,7 @@ contract("FeeSharingCollector:", (accounts) => { const name = "Test SOVToken"; const symbol = "TST"; - let NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT; + let RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT; let root, account1, account2, account3, account4; let SOVToken, SUSD, WrappedNativeToken, sovryn, staking; let loanTokenSettings, loanTokenLogic, loanToken; @@ -320,8 +320,8 @@ contract("FeeSharingCollector:", (accounts) => { const maxDisagreement = new BN(wei("5", "ether")); await sovryn.setMaxDisagreement(maxDisagreement); - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT = - await feeSharingCollector.NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT(); + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT = + await feeSharingCollector.RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT(); await feeSharingCollector.initialize( WrappedNativeToken.address, @@ -571,7 +571,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(processedCheckpoints.toNumber()).to.equal(10); }); - it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non-nativeToken based tokens", async () => { + it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non native token based tokens", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 /// NativeToken @@ -589,7 +589,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -615,7 +615,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, { @@ -635,7 +635,7 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); @@ -657,7 +657,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(processedCheckpointsSOV.toNumber()).to.equal(10); }); - it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non-nativeToken based tokens (withdraw partially)", async () => { + it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non native token based tokens (withdraw partially)", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 /// NativeToken @@ -675,7 +675,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -697,7 +697,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, { @@ -717,7 +717,7 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); @@ -795,7 +795,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -805,7 +805,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -821,7 +821,7 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); }); @@ -841,7 +841,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -851,7 +851,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -867,7 +867,7 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.equal(10); }); @@ -887,7 +887,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextPositive = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -897,7 +897,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: nextPositive.checkpointNum.toNumber(), }, ], @@ -908,13 +908,13 @@ contract("FeeSharingCollector:", (accounts) => { let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); /** Checkpoints won't be processed, stays at 0 */ expect(processedCheckpoints.toNumber()).to.equal(0); }); - it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non-nativeToken token is passed", async () => { + it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non native token token is passed", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 /// NativeToken @@ -943,11 +943,11 @@ contract("FeeSharingCollector:", (accounts) => { ZERO_ADDRESS, { from: account1 } ), - "only nativeToken-based tokens are allowed" + "only native token based tokens are allowed" ); }); - it("should not be able to pass non-nativeToken based token as _nativeTokensRegularWithdraw using claimAllCollectedFees() function", async () => { + it("should not be able to pass non native token based token as _nativeTokensRegularWithdraw using claimAllCollectedFees() function", async () => { // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3 /// NativeToken @@ -976,7 +976,7 @@ contract("FeeSharingCollector:", (accounts) => { ZERO_ADDRESS, { from: account1 } ), - "only nativeToken-based tokens are allowed" + "only native token based tokens are allowed" ); }); @@ -992,7 +992,7 @@ contract("FeeSharingCollector:", (accounts) => { const nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1003,7 +1003,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectRevert( feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, 0 ), @@ -1180,7 +1180,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 0, }, ], @@ -1247,7 +1247,7 @@ contract("FeeSharingCollector:", (accounts) => { await feeSharingCollector.setUserProcessedCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 2 ); @@ -1257,7 +1257,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 2, }, ], @@ -1336,19 +1336,16 @@ contract("FeeSharingCollector:", (accounts) => { // NativeToken for (let i = 0; i < 2; i++) { await feeSharingCollector.addCheckPoint( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, userStake ); await increaseTime(FEE_WITHDRAWAL_INTERVAL); await mineBlock(); } - await feeSharingCollector.addCheckPoint( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, - userStake - ); + await feeSharingCollector.addCheckPoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, userStake); await feeSharingCollector.setUserProcessedCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); await expectRevert( @@ -1357,7 +1354,7 @@ contract("FeeSharingCollector:", (accounts) => { [], [ { - tokenAddress: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + tokenAddress: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, fromCheckpoint: 2, }, ], @@ -1500,7 +1497,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1516,7 +1513,7 @@ contract("FeeSharingCollector:", (accounts) => { nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1528,13 +1525,13 @@ contract("FeeSharingCollector:", (accounts) => { await feeSharingCollector.setUserProcessedCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 9 ); nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1547,7 +1544,7 @@ contract("FeeSharingCollector:", (accounts) => { // undo mock user processed checkpoints await feeSharingCollector.setUserProcessedCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); @@ -1557,7 +1554,7 @@ contract("FeeSharingCollector:", (accounts) => { nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -1601,20 +1598,20 @@ contract("FeeSharingCollector:", (accounts) => { .equal(0); await feeSharingCollector.setTotalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 5 ); expect( ( await feeSharingCollector.totalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ) ).toNumber() ) .equal( ( await feeSharingCollector.numTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ) ).toNumber() ) @@ -1859,11 +1856,11 @@ contract("FeeSharingCollector:", (accounts) => { //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -1873,7 +1870,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString()); // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -1939,11 +1936,11 @@ contract("FeeSharingCollector:", (accounts) => { //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -1954,7 +1951,7 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -2090,11 +2087,11 @@ contract("FeeSharingCollector:", (accounts) => { // checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -2105,7 +2102,7 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -2185,11 +2182,11 @@ contract("FeeSharingCollector:", (accounts) => { // checkpoints totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -2202,7 +2199,7 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -2363,7 +2360,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2372,7 +2369,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectRevert( feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 0 ), @@ -2390,7 +2387,7 @@ contract("FeeSharingCollector:", (accounts) => { let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2402,7 +2399,7 @@ contract("FeeSharingCollector:", (accounts) => { const allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, MAX_NEXT_POSITIVE_CHECKPOINT ); @@ -2518,12 +2515,12 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, 10000000 ); @@ -2533,7 +2530,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2546,13 +2543,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1000 ); @@ -2582,7 +2579,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2592,7 +2589,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2603,7 +2600,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2613,7 +2610,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2626,13 +2623,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2662,7 +2659,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2672,7 +2669,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2683,7 +2680,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2693,7 +2690,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2706,13 +2703,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2742,7 +2739,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2752,7 +2749,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2763,7 +2760,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2773,7 +2770,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, ZERO_ADDRESS, @@ -2786,13 +2783,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 10, 1 ); @@ -2822,7 +2819,7 @@ contract("FeeSharingCollector:", (accounts) => { await mine(2880 * 15, { interval: 30 }); // 86400 (1day) / 30 == 2800 * 15 (2 weeks + 1 day - for weighted stake to be updated in cache of FeeSharingCollector._getAccumulatedFees()) const totalCheckpoints = await feeSharingCollector.totalTokenCheckpoints( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let iteration = 0; @@ -2832,7 +2829,7 @@ contract("FeeSharingCollector:", (accounts) => { let allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0, maxCheckpoint ); @@ -2843,7 +2840,7 @@ contract("FeeSharingCollector:", (accounts) => { for (let i = 0; i < totalCheckpoints; i += maxCheckpoint) { const fees = await feeSharingCollector.getAccumulatedFeesForCheckpointsRange( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, i, maxCheckpoint ); @@ -2853,7 +2850,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 2, ZERO_ADDRESS, @@ -2866,13 +2863,13 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(2); allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, 100 ); @@ -2886,7 +2883,7 @@ contract("FeeSharingCollector:", (accounts) => { } allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, tempMaxCheckpoint ); @@ -2900,7 +2897,7 @@ contract("FeeSharingCollector:", (accounts) => { } allUserFees = await feeSharingCollector.getAllUserFeesPerMaxCheckpoints( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 2, tempMaxCheckpoint ); @@ -3099,7 +3096,7 @@ contract("FeeSharingCollector:", (accounts) => { ); }); - it("Should not be able to pass non-nativeToken based token as _nativeTokensRegularWithdraw in claimAllCollectedFees() function", async () => { + it("Should not be able to pass non native token based token as _nativeTokensRegularWithdraw in claimAllCollectedFees() function", async () => { await protocolDeploymentFixture(); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -3129,7 +3126,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); @@ -3144,7 +3141,7 @@ contract("FeeSharingCollector:", (accounts) => { from: account1, } ), - "only nativeToken-based tokens are allowed" + "only native token based tokens are allowed" ); }); @@ -3178,13 +3175,13 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3201,7 +3198,7 @@ contract("FeeSharingCollector:", (accounts) => { ] = await Promise.all([ feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ), feeSharingCollector.processedCheckpoints.call( account1, @@ -3315,13 +3312,13 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10))); let tx = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3338,7 +3335,7 @@ contract("FeeSharingCollector:", (accounts) => { ] = await Promise.all([ feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ), feeSharingCollector.processedCheckpoints.call( account1, @@ -3423,7 +3420,7 @@ contract("FeeSharingCollector:", (accounts) => { const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); const accumulatedFeesWrappedNativeToken = await feeSharingCollector.getAccumulatedFees.call( @@ -3444,7 +3441,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) @@ -3453,7 +3450,7 @@ contract("FeeSharingCollector:", (accounts) => { /** Withdraw NativeToken */ let tx1 = await feeSharingCollector.claimAllCollectedFees( [], - [NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT], + [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT], [], 1000, account2, @@ -3493,7 +3490,7 @@ contract("FeeSharingCollector:", (accounts) => { ] = await Promise.all([ feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ), feeSharingCollector.processedCheckpoints.call( account1, @@ -3596,7 +3593,7 @@ contract("FeeSharingCollector:", (accounts) => { const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); const accumulatedFeesWrappedNativeToken = await feeSharingCollector.getAccumulatedFees.call( @@ -3617,7 +3614,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) @@ -3627,7 +3624,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -3647,7 +3644,7 @@ contract("FeeSharingCollector:", (accounts) => { ] = await Promise.all([ feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ), feeSharingCollector.processedCheckpoints.call( account1, @@ -3734,7 +3731,7 @@ contract("FeeSharingCollector:", (accounts) => { const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); const accumulatedFeesWrappedNativeToken = await feeSharingCollector.getAccumulatedFees.call( @@ -3755,7 +3752,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) @@ -3878,7 +3875,7 @@ contract("FeeSharingCollector:", (accounts) => { const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); const accumulatedFeesWrappedNativeToken = await feeSharingCollector.getAccumulatedFees.call( @@ -3899,7 +3896,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal( new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10)) @@ -3911,7 +3908,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -3927,7 +3924,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx2 = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -3947,7 +3944,7 @@ contract("FeeSharingCollector:", (accounts) => { ] = await Promise.all([ feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ), feeSharingCollector.processedCheckpoints.call( account1, @@ -4012,7 +4009,7 @@ contract("FeeSharingCollector:", (accounts) => { let fees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10))); @@ -4020,7 +4017,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4066,7 +4063,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(1); @@ -4403,7 +4400,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4433,7 +4430,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(1); @@ -4491,7 +4488,7 @@ contract("FeeSharingCollector:", (accounts) => { tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4511,7 +4508,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(3); @@ -4555,7 +4552,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4570,7 +4567,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); }); @@ -4594,7 +4591,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4609,14 +4606,14 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(5); tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4631,14 +4628,14 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(8); tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4653,7 +4650,7 @@ contract("FeeSharingCollector:", (accounts) => { // processedCheckpoints processedCheckpoints = await feeSharingCollector.processedCheckpoints.call( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(processedCheckpoints.toNumber()).to.be.equal(10); }); @@ -4756,7 +4753,7 @@ contract("FeeSharingCollector:", (accounts) => { let tx = await feeSharingCollector.claimAllCollectedFees( [], [ - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WrappedNativeToken.address, loanWrappedNativeToken.address, ], @@ -4842,7 +4839,7 @@ contract("FeeSharingCollector:", (accounts) => { let feesWithdrawn = tx.logs[1].args.amount; let userFees = await feeSharingCollector.getAccumulatedFees( account1, - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); // 100% of the fees should go to the user -> vesting contract not considered @@ -5007,11 +5004,11 @@ contract("FeeSharingCollector:", (accounts) => { //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5022,7 +5019,7 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -5096,11 +5093,11 @@ contract("FeeSharingCollector:", (accounts) => { //checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5111,7 +5108,7 @@ contract("FeeSharingCollector:", (accounts) => { //check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -5375,9 +5372,7 @@ contract("FeeSharingCollector:", (accounts) => { await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root); expect(totalAccumulatedNativeTokenFee.toNumber()).to.equal(0); expect( - await feeSharingCollector.unprocessedAmount.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ) + await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) ).to.be.bignumber.equal(new BN(0)); }); @@ -5395,9 +5390,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString()); expect( - await feeSharingCollector.unprocessedAmount.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ) + await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) ).to.be.bignumber.equal(new BN(0)); expectEvent(tx, "TokensTransferred", { @@ -5408,11 +5401,11 @@ contract("FeeSharingCollector:", (accounts) => { // checkpoints let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(1); let checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 0 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5423,14 +5416,14 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); let block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); expectEvent(tx, "CheckpointAdded", { sender: root, - token: NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount: new BN(amount), }); @@ -5443,9 +5436,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString()); expect( - await feeSharingCollector.unprocessedAmount.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ) + await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) ).to.be.bignumber.equal(new BN(amount * 2)); expectEvent(tx, "TokensTransferred", { @@ -5467,18 +5458,16 @@ contract("FeeSharingCollector:", (accounts) => { ); expect( - await feeSharingCollector.unprocessedAmount.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT - ) + await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT) ).to.be.bignumber.equal(new BN(0)); // checkpoints totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); expect(totalTokenCheckpoints.toNumber()).to.be.equal(2); checkpoint = await feeSharingCollector.tokenCheckpoints.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT, + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, 1 ); expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber); @@ -5489,7 +5478,7 @@ contract("FeeSharingCollector:", (accounts) => { // check lastFeeWithdrawalTime lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call( - NATIVE_TOKEN_DUMMY_ADDRESS_FOR_CHECKPOINT + RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ); block = await web3.eth.getBlock(tx.receipt.blockNumber); expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString()); @@ -5519,65 +5508,6 @@ contract("FeeSharingCollector:", (accounts) => { gas: 50000, }); }); - - it("recoverIncorrectAllocatedFees() can only be called by the owner", async () => { - await protocolDeploymentFixture(); - await expectRevert( - feeSharingCollector.recoverIncorrectAllocatedFees({ from: accounts[1] }), - "unauthorized" - ); - }); - - it("recoverIncorrectAllocatedFees() can only be executed once", async () => { - const owner = root; - await protocolDeploymentFixture(); - await feeSharingCollector.recoverIncorrectAllocatedFees({ from: owner }); - await expectRevert( - feeSharingCollector.recoverIncorrectAllocatedFees({ from: owner }), - "FeeSharingCollector: function can only be called once" - ); - }); - - it("Should be able to withdraw the incorrect allocated fees properly", async () => { - await protocolDeploymentFixture(); - const owner = await feeSharingCollector.owner(); - const previousBalanceOwner = new BN(await web3.eth.getBalance(owner)); - const tx = await feeSharingCollector.recoverIncorrectAllocatedFees(); - const latestBalanceOwner = new BN(await web3.eth.getBalance(owner)); - const txFee = new BN((await etherGasCost(tx.receipt)).toString()); - - expect(previousBalanceOwner.add(nativeTokenAmount).sub(txFee).toString()).to.be.equal( - latestBalanceOwner.toString() - ); - }); - - it("Should revert if sov or zusd transfer failed", async () => { - await protocolDeploymentFixture(); - mockSOV.transfer.returns(false); - await expectRevert( - feeSharingCollector.recoverIncorrectAllocatedFees(), - "SafeERC20: ERC20 operation did not succeed" - ); - mockSOV.transfer.returns(true); - mockZUSD.transfer.returns(false); - await expectRevert( - feeSharingCollector.recoverIncorrectAllocatedFees(), - "SafeERC20: ERC20 operation did not succeed" - ); - }); - - it("Should revert if nativeToken transfer failed", async () => { - feeSharingCollector = await FeeSharingCollectorMockup.new( - sovryn.address, - staking.address - ); - - /** Should revert because feeSharingCollector does not have enough balance of nativeToken */ - await expectRevert( - feeSharingCollector.recoverIncorrectAllocatedFees(), - "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal nativeToken failed" - ); - }); }); describe("test coverage", async () => { @@ -5677,7 +5607,7 @@ contract("FeeSharingCollector:", (accounts) => { expect(hasFees).to.equal(false); }); - it("getNativeTokenBalance should revert error if non-nativeToken token is passed", async () => { + it("getNativeTokenBalance should revert error if non native token token is passed", async () => { await protocolDeploymentFixture(); feeSharingCollector = await FeeSharingCollectorMockup.new( sovryn.address, @@ -5686,7 +5616,7 @@ contract("FeeSharingCollector:", (accounts) => { await expectRevert( feeSharingCollector.getNativeTokenBalance(SOVToken.address, root, 0), - "FeeSharingCollector::_getNativeTokenBalance: only nativeToken-based tokens are allowed" + "FeeSharingCollector::_getNativeTokenBalance: only native token based tokens are allowed" ); }); From 8778ed7246c62820992035bae93af27d68576a28 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Tue, 2 Jul 2024 16:51:59 +0700 Subject: [PATCH 09/15] change tokens withdraw interaction --- .../FeeSharingCollectorMultiToken.sol | 9 +++------ contracts/mockup/MockSovrynDexMultipleToken.sol | 9 ++++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol index 7b1ea3731..b9388d07f 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol @@ -217,12 +217,9 @@ contract FeeSharingCollectorMultiToken is ); } - for (uint256 i = 0; i < _tokens.length; i++) { - address token = _tokens[i]; - /** Withdraw from dex */ - bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, token); - ISovrynDex(sovrynDexAddress).userCmd(SOVRYN_DEX_COLD_PATH_PROXY_IDX, cmd); - } + /** Withdraw from dex */ + bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, _tokens); + ISovrynDex(sovrynDexAddress).userCmd(SOVRYN_DEX_COLD_PATH_PROXY_IDX, cmd); } /** diff --git a/contracts/mockup/MockSovrynDexMultipleToken.sol b/contracts/mockup/MockSovrynDexMultipleToken.sol index 55c5bb8c7..42fac35b0 100644 --- a/contracts/mockup/MockSovrynDexMultipleToken.sol +++ b/contracts/mockup/MockSovrynDexMultipleToken.sol @@ -22,13 +22,16 @@ contract MockSovrynDexMultiToken { function userCmd(uint16 callpath, bytes calldata cmd) external payable { require(msg.sender == treasury, "Only Treasury"); - (uint8 cmdCode, address token) = abi.decode(cmd, (uint8, address)); + (uint8 cmdCode, address[] memory tokens) = abi.decode(cmd, (uint8, address[])); if ( callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE ) { - IERC20(token).approve(treasury, tokenFees[token]); - IFeeSharingCollectorMultiToken(treasury).transferTokens(token, tokenFees[token]); + for(uint256 i = 0; i < tokens.length; i++) { + address token = tokens[i]; + IERC20(token).approve(treasury, tokenFees[token]); + IFeeSharingCollectorMultiToken(treasury).transferTokens(token, tokenFees[token]); + } } } From cacbe7917f0bcdf0bb83554cac4801f78d08e9d0 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Tue, 2 Jul 2024 16:52:16 +0700 Subject: [PATCH 10/15] ran prettier --- contracts/mockup/MockSovrynDexMultipleToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mockup/MockSovrynDexMultipleToken.sol b/contracts/mockup/MockSovrynDexMultipleToken.sol index 42fac35b0..ddfee3ac4 100644 --- a/contracts/mockup/MockSovrynDexMultipleToken.sol +++ b/contracts/mockup/MockSovrynDexMultipleToken.sol @@ -27,7 +27,7 @@ contract MockSovrynDexMultiToken { callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE ) { - for(uint256 i = 0; i < tokens.length; i++) { + for (uint256 i = 0; i < tokens.length; i++) { address token = tokens[i]; IERC20(token).approve(treasury, tokenFees[token]); IFeeSharingCollectorMultiToken(treasury).transferTokens(token, tokenFees[token]); From 084dd92b4d20a220a19c6525a74554ec0a25060d Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Wed, 11 Sep 2024 22:19:22 +0700 Subject: [PATCH 11/15] feat: update for the lb dex integration --- .../FeeSharingCollectorMultiToken.sol | 71 ++++++--- .../FeeSharingCollectorStorage.sol | 4 +- contracts/interfaces/ISovrynDex.sol | 5 - contracts/interfaces/ISovrynLBFactoryDex.sol | 9 ++ contracts/interfaces/ISovrynLBPairDex.sol | 11 ++ contracts/mockup/MockSovrynLBFactory.sol | 34 +++++ contracts/mockup/MockSovrynLBPair.sol | 53 +++++++ tests/FeeSharingCollectorMultiToken.test.js | 140 +++++++++++------- 8 files changed, 245 insertions(+), 82 deletions(-) delete mode 100644 contracts/interfaces/ISovrynDex.sol create mode 100644 contracts/interfaces/ISovrynLBFactoryDex.sol create mode 100644 contracts/interfaces/ISovrynLBPairDex.sol create mode 100644 contracts/mockup/MockSovrynLBFactory.sol create mode 100644 contracts/mockup/MockSovrynLBPair.sol diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol index b9388d07f..50fe24563 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol @@ -8,7 +8,8 @@ import "../../openzeppelin/Ownable.sol"; import "../IFeeSharingCollectorMultiToken.sol"; import "../../openzeppelin/Address.sol"; import "./FeeSharingCollectorStorage.sol"; -import "../../interfaces/ISovrynDex.sol"; +import "../../interfaces/ISovrynLBFactoryDex.sol"; +import "../../interfaces/ISovrynLBPairDex.sol"; /** * @title The FeeSharingCollectorMultiToken contract. @@ -86,10 +87,10 @@ contract FeeSharingCollectorMultiToken is event SetProtocolAddress(address indexed sender, address _protocolAddress); - event SetSovrynDexAddress( + event SetSovrynLBDexFactoryAddress( address indexed sender, - address indexed oldSovrynDexAddress, - address indexed newSovrynDexAddress + address indexed oldSovrynLBDexFactoryAddress, + address indexed newSovrynLBDexFactoryAddress ); /* Functions */ @@ -106,7 +107,7 @@ contract FeeSharingCollectorMultiToken is address dexAddress ) external onlyOwner oneTimeExecution(this.initialize.selector) { setWrappedNativeToken(wrappedNativeToken); - setSovrynDexAddress(dexAddress); + setSovrynLBDexFactoryAddress(dexAddress); } /** @@ -145,10 +146,17 @@ contract FeeSharingCollectorMultiToken is emit SetProtocolAddress(msg.sender, _protocolAddress); } - function setSovrynDexAddress(address _sovrynDexAddress) public onlyOwner { - require(Address.isContract(_sovrynDexAddress), "_sovrynDexAddress not a contract"); - emit SetSovrynDexAddress(msg.sender, sovrynDexAddress, _sovrynDexAddress); - sovrynDexAddress = _sovrynDexAddress; + function setSovrynLBDexFactoryAddress(address _sovrynLBDexFactoryAddress) public onlyOwner { + require( + Address.isContract(_sovrynLBDexFactoryAddress), + "_sovrynLBDexFactoryAddress not a contract" + ); + emit SetSovrynLBDexFactoryAddress( + msg.sender, + sovrynLBDexFactoryAddress, + _sovrynLBDexFactoryAddress + ); + sovrynLBDexFactoryAddress = _sovrynLBDexFactoryAddress; } /** @@ -203,23 +211,44 @@ contract FeeSharingCollectorMultiToken is } /** - * @notice Withdraw fees from Sovryn DEX - * protocolFee from the conversion + * @notice Withdraw fees from sovryn-lb-dex + * * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool * - * @param _tokens array addresses of the tokens * */ - function withdrawFeesFromDex(address[] memory _tokens) public { - for (uint256 i = 0; i < _tokens.length; i++) { - require( - Address.isContract(_tokens[i]), - "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" + function withdrawFeesFromLBDex() public { + /** Get all of the pairs that has > 0 protocol fee */ + uint256 totalPairs = ISovrynLBFactoryDex(sovrynLBDexFactoryAddress).getNumberOfLBPairs(); + for (uint256 i = 0; i < totalPairs; i++) { + address lbPair = ISovrynLBFactoryDex(sovrynLBDexFactoryAddress).getLBPairAtIndex(i); + (uint128 feeX, uint128 feeY) = ISovrynLBPairDex(lbPair).getProtocolFees(); + + /** Skip if lbPair contains 0 protocol fees */ + if (feeX == 0 && feeY == 0) continue; + + /** Withdraw fee */ + ISovrynLBPairDex(lbPair).collectProtocolFees(); + + /** Get tokenX & tokenY */ + address tokenX = ISovrynLBPairDex(lbPair).getTokenX(); + address tokenY = ISovrynLBPairDex(lbPair).getTokenY(); + + /** Add checkpoint for the token */ + _addCheckpoint( + tokenX, + safe96( + uint256(feeX), + "FeeSharingProxy::withdrawFeesFromLBDex: tokenFeeX amount exceeds 96 bits" + ) + ); + _addCheckpoint( + tokenY, + safe96( + uint256(feeY), + "FeeSharingProxy::withdrawFeesFromLBDex: tokenFeeY amount exceeds 96 bits" + ) ); } - - /** Withdraw from dex */ - bytes memory cmd = abi.encode(SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE, _tokens); - ISovrynDex(sovrynDexAddress).userCmd(SOVRYN_DEX_COLD_PATH_PROXY_IDX, cmd); } /** diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol index dcd6f726b..ea94f919f 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol @@ -88,9 +88,9 @@ contract FeeSharingCollectorStorage is Ownable { address public loanWrappedNativeTokenAddress; /** - * @dev sovrynDex + * @dev sovryn lb dex factory address */ - address public sovrynDexAddress; + address public sovrynLBDexFactoryAddress; /** * @dev Prevents a contract from calling itself, directly or indirectly. diff --git a/contracts/interfaces/ISovrynDex.sol b/contracts/interfaces/ISovrynDex.sol deleted file mode 100644 index 4ea0b63b2..000000000 --- a/contracts/interfaces/ISovrynDex.sol +++ /dev/null @@ -1,5 +0,0 @@ -pragma solidity 0.5.17; - -interface ISovrynDex { - function userCmd(uint16 callpath, bytes calldata cmd) external payable; -} diff --git a/contracts/interfaces/ISovrynLBFactoryDex.sol b/contracts/interfaces/ISovrynLBFactoryDex.sol new file mode 100644 index 000000000..cc2cd359e --- /dev/null +++ b/contracts/interfaces/ISovrynLBFactoryDex.sol @@ -0,0 +1,9 @@ +pragma solidity 0.5.17; + +interface ISovrynLBFactoryDex { + function getNumberOfLBPairs() external view returns (uint256); + + function getLBPairAtIndex(uint256 id) external returns (address); + + function getFeeRecipient() external view returns (address); +} diff --git a/contracts/interfaces/ISovrynLBPairDex.sol b/contracts/interfaces/ISovrynLBPairDex.sol new file mode 100644 index 000000000..4df6d730e --- /dev/null +++ b/contracts/interfaces/ISovrynLBPairDex.sol @@ -0,0 +1,11 @@ +pragma solidity 0.5.17; + +interface ISovrynLBPairDex { + function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY); + + function collectProtocolFees() external returns (bytes32 collectedProtocolFees); + + function getTokenX() external view returns (address tokenX); + + function getTokenY() external view returns (address tokenY); +} diff --git a/contracts/mockup/MockSovrynLBFactory.sol b/contracts/mockup/MockSovrynLBFactory.sol new file mode 100644 index 000000000..fef4ecf47 --- /dev/null +++ b/contracts/mockup/MockSovrynLBFactory.sol @@ -0,0 +1,34 @@ +pragma solidity 0.5.17; + +import "../interfaces/IERC20.sol"; +import "../governance/IFeeSharingCollectorMultiToken.sol"; + +contract MockSovrynLBFactory { + address feeRecipient; + uint256 totalPairs; + address[] lbPairs; + + constructor() public {} + + function getFeeRecipient() public returns (address) { + return feeRecipient; + } + + function setFeeRecipient(address _feeRecipient) public { + feeRecipient = _feeRecipient; + } + + function addLbPairs(uint256 _totalPairs, address[] memory _lbPairs) public { + require(_totalPairs == _lbPairs.length, "mismatch lbPairs length"); + totalPairs = _totalPairs; + lbPairs = _lbPairs; + } + + function getNumberOfLBPairs() public view returns (uint256) { + return totalPairs; + } + + function getLBPairAtIndex(uint256 _id) public view returns (address) { + return lbPairs[_id]; + } +} diff --git a/contracts/mockup/MockSovrynLBPair.sol b/contracts/mockup/MockSovrynLBPair.sol new file mode 100644 index 000000000..80423fb2a --- /dev/null +++ b/contracts/mockup/MockSovrynLBPair.sol @@ -0,0 +1,53 @@ +pragma solidity 0.5.17; + +import "../interfaces/IERC20.sol"; +import "../governance/IFeeSharingCollectorMultiToken.sol"; +import "../interfaces/ISovrynLBFactoryDex.sol"; + +contract MockSovrynLBPair { + address lbFactory; + address tokenX; + address tokenY; + + uint128 amountTokenX; + uint128 amountTokenY; + + modifier onlyFeeRecipient() { + require( + ISovrynLBFactoryDex(lbFactory).getFeeRecipient() == msg.sender, + "Only feeRecipient" + ); + _; + } + + function getTokenX() public returns (address _tokenX) { + return tokenX; + } + + function getTokenY() public returns (address _tokenY) { + return tokenY; + } + + constructor( + address _lbFactory, + address _tokenX, + address _tokenY, + uint128 _amountTokenX, + uint128 _amountTokenY + ) public { + tokenX = _tokenX; + tokenY = _tokenY; + amountTokenX = _amountTokenX; + amountTokenY = _amountTokenY; + lbFactory = _lbFactory; + } + + function getProtocolFees() public view returns (uint128 protocolFeeX, uint128 protocolFeeY) { + return (amountTokenX, amountTokenY); + } + + function collectProtocolFees() public onlyFeeRecipient returns (bytes32) { + IERC20(tokenX).transfer(msg.sender, amountTokenX); + IERC20(tokenY).transfer(msg.sender, amountTokenY); + } +} diff --git a/tests/FeeSharingCollectorMultiToken.test.js b/tests/FeeSharingCollectorMultiToken.test.js index 84ddc6a1b..a492bd806 100644 --- a/tests/FeeSharingCollectorMultiToken.test.js +++ b/tests/FeeSharingCollectorMultiToken.test.js @@ -39,7 +39,8 @@ const LockedSOV = artifacts.require("LockedSOV"); const FeeSharingCollector = artifacts.require("FeeSharingCollectorMultiToken"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMultiTokenMockup"); -const MockSovrynDex = artifacts.require("MockSovrynDexMultiToken"); +const MockSovrynLBFactory = artifacts.require("MockSovrynLBFactory"); +const MockSovrynLBPair = artifacts.require("MockSovrynLBPair"); const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup"); const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup"); @@ -103,7 +104,7 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { let loanWrappedNativeToken; let tradingFeePercent; let mockPrice; - let sovrynDex; + let sovrynLBFactory; before(async () => { [root, account1, account2, account3, account4, ...accounts] = accounts; @@ -289,9 +290,9 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { const maxDisagreement = new BN(wei("5", "ether")); await sovryn.setMaxDisagreement(maxDisagreement); - sovrynDex = await MockSovrynDex.new(); + sovrynLBFactory = await MockSovrynLBFactory.new(); - await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynLBFactory.address); return sovryn; } @@ -331,7 +332,7 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100) ).address; await expectRevert( - feeSharingCollector.initialize(wrappedNativeTokenAddress, sovrynDex.address), + feeSharingCollector.initialize(wrappedNativeTokenAddress, sovrynLBFactory.address), "function can only be called once" ); }); @@ -899,17 +900,7 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { }); }); - describe("withdraw Dex Fees", async () => { - it("should not be able to withdraw fees if invalid token address", async () => { - /// @dev This test requires redeploying the protocol - await protocolDeploymentFixture(); - - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([accounts[0]]), - "FeeSharingCollector::withdrawFeesFromDex: token is not a contract" - ); - }); - + describe("withdraw LB Dex Fees", async () => { it("Should be able to withdraw Dex Fees", async () => { /// @dev This test requires redeploying the protocol await protocolDeploymentFixture(); @@ -918,61 +909,96 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { let totalStake = 1000; await stake(totalStake, root); - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([SUSD.address]), - "Only Treasury" + const lbPair1Amount = new BN(wei("1", "ether")); + const lbPair2Amount = new BN(wei("2", "ether")); + + const lbPair1 = await MockSovrynLBPair.new( + sovrynLBFactory.address, + SUSD.address, + WrappedNativeToken.address, + lbPair1Amount, + lbPair1Amount + ); + const lbPair2 = await MockSovrynLBPair.new( + sovrynLBFactory.address, + SUSD.address, + SOVToken.address, + lbPair2Amount, + lbPair2Amount ); //mock data - const feeAmount = new BN(wei("1", "ether")); - await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); - await sovrynDex.setTreasury(feeSharingCollector.address); - await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + const totalFeeAmount = lbPair1Amount.add(lbPair2Amount); + await SUSD.mint(lbPair1.address, wei("2000", "ether")); + await WrappedNativeToken.mint(lbPair1.address, wei("2000", "ether")); + await SUSD.mint(lbPair2.address, wei("2000", "ether")); + await SOVToken.mint(lbPair2.address, wei("2000", "ether")); - await SUSD.mint(sovrynDex.address, wei("2000", "ether")); + await sovrynLBFactory.addLbPairs(2, [lbPair1.address, lbPair2.address]); - let previousProtocolBalance = await SUSD.balanceOf(sovrynDex.address); - let previousFeeSharingCollectorBalance = await SUSD.balanceOf( + await expectRevert(feeSharingCollector.withdrawFeesFromLBDex(), "Only feeRecipient"); + + await sovrynLBFactory.setFeeRecipient(feeSharingCollector.address); + + let previousLBPair1BalanceSUSD = await SUSD.balanceOf(lbPair1.address); + let previousLBPair2BalanceSUSD = await SUSD.balanceOf(lbPair2.address); + let previousFeeSharingCollectorBalanceSUSD = await SUSD.balanceOf( feeSharingCollector.address ); - tx = await feeSharingCollector.withdrawFeesFromDex([SUSD.address]); - let latestProtocolBalance = await SUSD.balanceOf(sovrynDex.address); - let latestFeeSharingCollectorBalance = await SUSD.balanceOf( + let previousLBPair1BalanceWrbtc = await WrappedNativeToken.balanceOf(lbPair1.address); + let previousFeeSharingCollectorBalanceWrbtc = await WrappedNativeToken.balanceOf( feeSharingCollector.address ); - expect(previousProtocolBalance.toString()).to.equal( - latestProtocolBalance.add(feeAmount).toString() + let previousLBPair2BalanceSov = await SOVToken.balanceOf(lbPair2.address); + let previousFeeSharingCollectorBalanceSov = await SOVToken.balanceOf( + feeSharingCollector.address ); - expect(previousFeeSharingCollectorBalance.toString()).to.equal("0"); - expect(latestFeeSharingCollectorBalance.toString()).to.equal(feeAmount.toString()); - }); + tx = await feeSharingCollector.withdrawFeesFromLBDex(); - it("Should not be able to withdraw with 0 AMM Fees", async () => { - /// @dev This test requires redeploying the protocol - await protocolDeploymentFixture(); + let latestLBPair1BalanceSUSD = await SUSD.balanceOf(lbPair1.address); + let latestLBPair2BalanceSUSD = await SUSD.balanceOf(lbPair2.address); + let latestFeeSharingCollectorBalanceSUSD = await SUSD.balanceOf( + feeSharingCollector.address + ); - //stake - getPriorTotalVotingPower - let totalStake = 1000; - await stake(totalStake, root); + let latestLBPair1BalanceWrbtc = await WrappedNativeToken.balanceOf(lbPair1.address); + let latestFeeSharingCollectorBalanceWrbtc = await WrappedNativeToken.balanceOf( + feeSharingCollector.address + ); - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([SUSD.address]), - "Only Treasury" + let latestLBPair2BalanceSov = await SOVToken.balanceOf(lbPair2.address); + let latestFeeSharingCollectorBalanceSov = await SOVToken.balanceOf( + feeSharingCollector.address ); - //mock data - const feeAmount = new BN(wei("0", "ether")); - await sovrynDex.setTokenDexFee(SUSD.address, feeAmount.toString()); - await sovrynDex.setTreasury(feeSharingCollector.address); - await sovrynDex.setWrbtcToken(WrappedNativeToken.address); + expect(previousLBPair1BalanceSUSD.toString()).to.equal( + latestLBPair1BalanceSUSD.add(lbPair1Amount).toString() + ); + expect(previousLBPair2BalanceSUSD.toString()).to.equal( + latestLBPair2BalanceSUSD.add(lbPair2Amount).toString() + ); + expect(previousFeeSharingCollectorBalanceSUSD.toString()).to.equal("0"); + expect(latestFeeSharingCollectorBalanceSUSD.toString()).to.equal( + totalFeeAmount.toString() + ); - await SUSD.mint(sovrynDex.address, wei("2", "ether")); - await expectRevert( - feeSharingCollector.withdrawFeesFromDex([SUSD.address]), - "FeeSharingCollectorMultiToken::transferTokens: invalid amount" + expect(previousLBPair1BalanceWrbtc.toString()).to.equal( + latestLBPair1BalanceWrbtc.add(lbPair1Amount).toString() + ); + expect(previousFeeSharingCollectorBalanceWrbtc.toString()).to.equal("0"); + expect(latestFeeSharingCollectorBalanceWrbtc.toString()).to.equal( + lbPair1Amount.toString() + ); + + expect(previousLBPair2BalanceSov.toString()).to.equal( + latestLBPair2BalanceSov.add(lbPair2Amount).toString() + ); + expect(previousFeeSharingCollectorBalanceSov.toString()).to.equal("0"); + expect(latestFeeSharingCollectorBalanceSov.toString()).to.equal( + lbPair2Amount.toString() ); }); }); @@ -1205,7 +1231,10 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { ); await sovryn.setFeesController(feeSharingCollector.address); - await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + sovrynLBFactory.address + ); // stake - getPriorTotalVotingPower let rootStake = 700; @@ -2000,7 +2029,10 @@ contract("FeeSharingCollectorMultiToken:", (accounts) => { ); await sovryn.setFeesController(feeSharingCollector.address); - await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynDex.address); + await feeSharingCollector.initialize( + WrappedNativeToken.address, + sovrynLBFactory.address + ); // stake - getPriorTotalVotingPower let rootStake = 700; From 6a0158ba8c9d4e46389637e1bf3919331e14d945 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Wed, 11 Sep 2024 22:50:37 +0700 Subject: [PATCH 12/15] removed unused contract --- contracts/mockup/MockSovrynDex.sol | 39 ------------------ .../mockup/MockSovrynDexMultipleToken.sol | 41 ------------------- 2 files changed, 80 deletions(-) delete mode 100644 contracts/mockup/MockSovrynDex.sol delete mode 100644 contracts/mockup/MockSovrynDexMultipleToken.sol diff --git a/contracts/mockup/MockSovrynDex.sol b/contracts/mockup/MockSovrynDex.sol deleted file mode 100644 index 36419233d..000000000 --- a/contracts/mockup/MockSovrynDex.sol +++ /dev/null @@ -1,39 +0,0 @@ -pragma solidity 0.5.17; - -import "../interfaces/IERC20.sol"; - -contract MockSovrynDex { - mapping(address => uint256) public tokenFees; - uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; - uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; - IERC20 wrbtcToken; - address treasury; - - constructor() public {} - - function setTreasury(address _treasury) public { - treasury = _treasury; - } - - function setWrbtcToken(IERC20 _wrbtcToken) public { - wrbtcToken = _wrbtcToken; - } - - function userCmd(uint16 callpath, bytes calldata cmd) external payable returns (bytes memory) { - require(msg.sender == treasury, "Only Treasury"); - (uint8 cmdCode, address token) = abi.decode(cmd, (uint8, address)); - if ( - callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && - cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE - ) { - wrbtcToken.transfer(msg.sender, tokenFees[token]); - return abi.encode(tokenFees[token]); - } else { - return "0x"; - } - } - - function setTokenDexFee(address token, uint256 fee) external { - tokenFees[token] = fee; - } -} diff --git a/contracts/mockup/MockSovrynDexMultipleToken.sol b/contracts/mockup/MockSovrynDexMultipleToken.sol deleted file mode 100644 index ddfee3ac4..000000000 --- a/contracts/mockup/MockSovrynDexMultipleToken.sol +++ /dev/null @@ -1,41 +0,0 @@ -pragma solidity 0.5.17; - -import "../interfaces/IERC20.sol"; -import "../governance/IFeeSharingCollectorMultiToken.sol"; - -contract MockSovrynDexMultiToken { - mapping(address => uint96) public tokenFees; - uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3; - uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40; - IERC20 wrbtcToken; - address treasury; - - constructor() public {} - - function setTreasury(address _treasury) public { - treasury = _treasury; - } - - function setWrbtcToken(IERC20 _wrbtcToken) public { - wrbtcToken = _wrbtcToken; - } - - function userCmd(uint16 callpath, bytes calldata cmd) external payable { - require(msg.sender == treasury, "Only Treasury"); - (uint8 cmdCode, address[] memory tokens) = abi.decode(cmd, (uint8, address[])); - if ( - callpath == SOVRYN_DEX_COLD_PATH_PROXY_IDX && - cmdCode == SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE - ) { - for (uint256 i = 0; i < tokens.length; i++) { - address token = tokens[i]; - IERC20(token).approve(treasury, tokenFees[token]); - IFeeSharingCollectorMultiToken(treasury).transferTokens(token, tokenFees[token]); - } - } - } - - function setTokenDexFee(address token, uint96 fee) external { - tokenFees[token] = fee; - } -} From 5bf61d6a44b7cdf2c2b6b160fc5d339b4fb42a42 Mon Sep 17 00:00:00 2001 From: Tyrone Johnson <tjcloa@gmail.com> Date: Tue, 24 Sep 2024 23:27:19 +0300 Subject: [PATCH 13/15] update vesting creation with new schedule --- hardhat/tasks/governance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat/tasks/governance.js b/hardhat/tasks/governance.js index 775365ce9..0dddd1c63 100644 --- a/hardhat/tasks/governance.js +++ b/hardhat/tasks/governance.js @@ -122,7 +122,7 @@ async function createVestings(hre, dryRun, path, multiplier, signerAcc) { vestingCreationType = 3; } else if (teamVesting[3] === 26) { vestingCreationType = 1; - } else if ([39, 22, 17, 34, 19].includes(teamVesting[3])) { + } else if ([39, 22, 17, 34, 19, 7, 24].includes(teamVesting[3])) { vestingCreationType = 5; console.log("Make sure 3 year team 2 vesting split is really expected!"); } else { From 02e1704aa4ed442a17f129fc08a961e52b5f408e Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Fri, 27 Sep 2024 10:21:01 +0700 Subject: [PATCH 14/15] addressed unresolved comments --- .../governance/FeeSharingCollector/FeeSharingCollector.sol | 2 +- .../FeeSharingCollector/FeeSharingCollectorMultiToken.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index 5c5d2ba27..b44ca6231 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -397,7 +397,7 @@ contract FeeSharingCollector is // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function which is burning to a native token - to be renamed to burnedToNativeToken ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false); } else { - // Previously it directly send the loanWrappedNativeToken to the user + // Previously it directly send the loanToken to the user require( IERC20(_token).transfer(_receiver, amount), "FeeSharingCollector::withdraw: withdrawal failed" diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol index 50fe24563..a481c16b8 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol @@ -417,7 +417,7 @@ contract FeeSharingCollectorMultiToken is processedCheckpoints[user][_token] = end; if (loanWrappedNativeToken == _token) { - // We will change, so that FeeSharingCollectorMultiToken will directly burn then loan wrapped native token to native token and send to the user --- by call burnToBTC function + // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function which is burning to a native token - to be renamed to burnedToNativeToken ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false); } else { require( From 1a4823e896190088056772e0209fb320d6784315 Mon Sep 17 00:00:00 2001 From: cwsnt <undefined@udefined.com> Date: Fri, 27 Sep 2024 10:29:32 +0700 Subject: [PATCH 15/15] 2/ addressed unresolved comments --- .../FeeSharingCollector.sol | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol index b44ca6231..482261cb1 100644 --- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol +++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol @@ -126,7 +126,7 @@ contract FeeSharingCollector is /** * @dev initialize function for fee sharing collector proxy - * @param wrappedNativeToken wrappedNativeToken token address + * @param wrappedNativeToken wrappedNativeToken address * @param loanWrappedNativeToken address of loan token wrappedNativeToken (IWrappedNativeToken) */ function initialize( @@ -142,11 +142,11 @@ contract FeeSharingCollector is } /** - * @notice Set the wrappedNativeToken token address of fee sharing collector. + * @notice Set the wrappedNativeToken address of fee sharing collector. * * only owner can perform this action. * - * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken token. + * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken. * */ function setWrappedNativeToken(address newWrappedNativeTokenAddress) public onlyOwner { require( @@ -178,11 +178,11 @@ contract FeeSharingCollector is } /** - * @notice Set the loan wrappedNativeToken token address of fee sharing collector. + * @notice Set the loan wrappedNativeToken address of fee sharing collector. * * only owner can perform this action. * - * @param newLoanWrappedNativeTokenAddress The new address of the loan wrappedNativeToken token. + * @param newLoanWrappedNativeTokenAddress The new address of the loan wrappedNativeToken. * */ function setLoanWrappedNativeToken(address newLoanWrappedNativeTokenAddress) public onlyOwner { require( @@ -226,7 +226,7 @@ contract FeeSharingCollector is /// @notice Update unprocessed amount of tokens uint96 amount96 = safe96( wrappedNativeTokenAmountWithdrawn, - "FeeSharingCollector::withdrawFees: wrappedNativeToken token amount exceeds 96 bits" + "FeeSharingCollector::withdrawFees: wrappedNativeToken amount exceeds 96 bits" ); _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96); @@ -267,13 +267,13 @@ contract FeeSharingCollector is /// @notice Update unprocessed amount of tokens uint96 amount96 = safe96( wrappedNativeTokenAmountWithdrawn, - "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken token amount exceeds 96 bits" + "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken amount exceeds 96 bits" ); totalPoolTokenAmount = add96( totalPoolTokenAmount, amount96, - "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken token amount exceeds 96 bits" + "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken amount exceeds 96 bits" ); emit FeeAMMWithdrawn( @@ -577,12 +577,12 @@ contract FeeSharingCollector is /** * @dev Function to wrap: - * 1. regular withdrawal for both nativeToken & non native token token + * 1. regular withdrawal for both native & non native token * 2. skipped checkpoints withdrawal for both native token & non native token * - * @param _nonNativeTokensRegularWithdraw array of non native token token address with no skipped checkpoints that will be withdrawn - * @param _nativeTokensRegularWithdraw array of nativeToken token address with no skipped checkpoints that will be withdrawn - * @param _tokensWithSkippedCheckpoints array of nativeToken & non native token TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn + * @param _nonNativeTokensRegularWithdraw array of non native token address with no skipped checkpoints that will be withdrawn + * @param _nativeTokensRegularWithdraw array of native token address with no skipped checkpoints that will be withdrawn + * @param _tokensWithSkippedCheckpoints array of native token & non native token TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn * */ function claimAllCollectedFees( @@ -607,7 +607,7 @@ contract FeeSharingCollector is ); } - /** Process normal non native token token withdrawal */ + /** Process normal non native token withdrawal */ for (uint256 i = 0; i < _nonNativeTokensRegularWithdraw.length; i++) { if (_maxCheckpoints == 0) break; uint256 endTokenCheckpoint;