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;