Repository: moondance-labs/tanssi-symbiotic
Pull request #57: [MD-944] Add Chainlink to get price of the underlying registered vault
Author: login: steven2308
Created: 2025-03-14T22:06:24Z | Merged: 2025-03-27T15:57:57Z



Diff:
diff --git a/.gitmodules b/.gitmodules
index 508b49f3..569dd464 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -32,3 +32,6 @@
 [submodule "lib/middleware-sdk"]
 	path = lib/middleware-sdk
 	url = https://github.com/symbioticfi/middleware-sdk
+[submodule "lib/chainlink-brownie-contracts"]
+	path = lib/chainlink-brownie-contracts
+	url = https://github.com/smartcontractkit/chainlink-brownie-contracts
diff --git a/foundry.toml b/foundry.toml
index 11ef9ea0..13dfabbb 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -23,6 +23,7 @@ remappings = [
     'openzeppelin-contracts/=lib/openzeppelin-contracts/',
     'openzeppelin/=lib/openzeppelin-contracts/contracts/',
     'prb/math/=lib/prb-math/',
+    "@chainlink/=lib/chainlink-brownie-contracts/contracts/src/v0.8/",
 ]
 
 [rpc_endpoints]
diff --git a/lib/chainlink-brownie-contracts b/lib/chainlink-brownie-contracts
new file mode 160000
index 00000000..f0e0d640
--- /dev/null
+++ b/lib/chainlink-brownie-contracts
@@ -0,0 +1 @@
+Subproject commit f0e0d640d7a22eccf904f54aa2c4a0e122df6100
diff --git a/script/DeployCollateral.s.sol b/script/DeployCollateral.s.sol
index 2504e5f2..762223af 100644
--- a/script/DeployCollateral.s.sol
+++ b/script/DeployCollateral.s.sol
@@ -28,7 +28,13 @@ contract DeployCollateral is Script {
     function deployCollateral(
         string memory tokenName
     ) public returns (address) {
-        Token token = new Token(tokenName);
+        Token token = new Token(tokenName, 18);
+
+        return address(token);
+    }
+
+    function deployCollateral(string memory tokenName, uint8 decimals) public returns (address) {
+        Token token = new Token(tokenName, decimals);
 
         return address(token);
     }
diff --git a/script/DeployTanssiEcosystem.s.sol b/script/DeployTanssiEcosystem.s.sol
index 182b5e5d..34304786 100644
--- a/script/DeployTanssiEcosystem.s.sol
+++ b/script/DeployTanssiEcosystem.s.sol
@@ -160,7 +160,7 @@ contract DeployTanssiEcosystem is Script {
         }
 
         // On real scenario we want to deploy only the slashable vault. TBD
-        if (isTest || block.chainid == 31_337 || block.chainid == 11_155_111) {
+        if (isTest || block.chainid == 31_337) {
             (vaultAddresses.vault, vaultAddresses.delegator, vaultAddresses.slasher) =
                 contractScripts.deployVault.createBaseVault(params);
             console2.log("Vault Collateral: ", IVault(vaultAddresses.vault).collateral());
@@ -170,7 +170,7 @@ contract DeployTanssiEcosystem is Script {
             console2.log(" ");
         }
 
-        if (block.chainid == 31_337 || block.chainid == 11_155_111) {
+        if (block.chainid == 31_337) {
             params.collateral = address(tokensAddresses.rETHToken);
         }
         (vaultAddresses.vaultSlashable, vaultAddresses.delegatorSlashable, vaultAddresses.slasherSlashable) =
@@ -181,9 +181,9 @@ contract DeployTanssiEcosystem is Script {
         console2.log("SlasherSlashable: ", vaultAddresses.slasherSlashable);
         console2.log(" ");
 
-        if (isTest || block.chainid == 31_337 || block.chainid == 11_155_111) {
+        if (isTest || block.chainid == 31_337) {
             params.delegatorIndex = DeployVault.DelegatorIndex.FULL_RESTAKE;
-            if (block.chainid == 31_337 || block.chainid == 11_155_111) {
+            if (block.chainid == 31_337) {
                 params.collateral = address(tokensAddresses.wBTCToken);
             }
             (vaultAddresses.vaultVetoed, vaultAddresses.delegatorVetoed, vaultAddresses.slasherVetoed) =
@@ -202,7 +202,7 @@ contract DeployTanssiEcosystem is Script {
     }
 
     function _setDelegatorConfigs() private {
-        if (block.chainid == 31_337 || block.chainid == 11_155_111 || isTest) {
+        if (block.chainid == 31_337 || isTest) {
             INetworkRestakeDelegator(vaultAddresses.delegator).setMaxNetworkLimit(0, MAX_NETWORK_LIMIT);
             INetworkRestakeDelegator(vaultAddresses.delegatorVetoed).setMaxNetworkLimit(0, MAX_NETWORK_LIMIT);
 
@@ -225,7 +225,7 @@ contract DeployTanssiEcosystem is Script {
             adminFeeClaimRoleHolder: tanssi,
             adminFeeSetRoleHolder: tanssi
         });
-        if (block.chainid == 31_337 || block.chainid == 11_155_111 || isTest) {
+        if (block.chainid == 31_337 || isTest) {
             ecosystemEntities.middleware.registerSharedVault(vaultAddresses.vault, stakerRewardsParams);
             ecosystemEntities.middleware.registerSharedVault(vaultAddresses.vaultVetoed, stakerRewardsParams);
         }
@@ -249,7 +249,7 @@ contract DeployTanssiEcosystem is Script {
             contractScripts.helperConfig.activeNetworkConfig();
 
         IDefaultCollateralFactory defaultCollateralFactory;
-        if (block.chainid != 31_337 && block.chainid != 11_155_111) {
+        if (block.chainid != 31_337) {
             defaultCollateralFactory = IDefaultCollateralFactory(defaultCollateralFactoryAddress);
             ecosystemEntities.defaultCollateralAddress =
                 defaultCollateralFactory.create(address(stETHAddress), 10_000 ether, address(0));
@@ -278,7 +278,7 @@ contract DeployTanssiEcosystem is Script {
 
         INetworkMiddlewareService networkMiddlewareService = INetworkMiddlewareService(networkMiddlewareServiceAddress);
 
-        if (block.chainid == 31_337 || block.chainid == 11_155_111) {
+        if (block.chainid == 31_337) {
             // Deploy simple ERC20 collateral tokens
             deployTokens(tanssi);
             _transferTokensToOperators();
diff --git a/script/HelperConfig.s.sol b/script/HelperConfig.s.sol
index 5d4d81c5..8839d255 100644
--- a/script/HelperConfig.s.sol
+++ b/script/HelperConfig.s.sol
@@ -36,7 +36,7 @@ contract HelperConfig is Script {
     uint256 public DEFAULT_ANVIL_PRIVATE_KEY = 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6;
 
     constructor() {
-        if (block.chainid == 31_337 || block.chainid == 11_155_111) {
+        if (block.chainid == 31_337) {
             activeNetworkConfig = getAnvilEthConfig();
         } else {
             activeNetworkConfig = getChainConfig();
diff --git a/src/contracts/extensions/OSharedVaults.sol b/src/contracts/extensions/OSharedVaults.sol
index 9c7c8971..d95f1881 100644
--- a/src/contracts/extensions/OSharedVaults.sol
+++ b/src/contracts/extensions/OSharedVaults.sol
@@ -20,8 +20,9 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
         address sharedVault,
         IODefaultStakerRewards.InitParams memory stakerRewardsParams
     ) public checkAccess {
-        _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
+        _beforeRegisterSharedVault(sharedVault);
         _registerSharedVault(sharedVault);
+        _afterRegisterSharedVault(sharedVault, stakerRewardsParams);
     }
 
     /**
@@ -57,9 +58,17 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
     /**
      * @notice Hook called before registering a shared vault
      * @param sharedVault The vault address
-     * @param stakerRewardsParams Init params to create a staker rewards contract associated with the shared vault
      */
     function _beforeRegisterSharedVault(
+        address sharedVault
+    ) internal virtual {}
+
+    /**
+     * @notice Hook called after registering a shared vault
+     * @param sharedVault The vault address
+     * @param stakerRewardsParams Init params to create a staker rewards contract associated with the shared vault
+     */
+    function _afterRegisterSharedVault(
         address sharedVault,
         IODefaultStakerRewards.InitParams memory stakerRewardsParams
     ) internal virtual {}
diff --git a/src/contracts/middleware/Middleware.sol b/src/contracts/middleware/Middleware.sol
index bddce94a..4ce8a361 100644
--- a/src/contracts/middleware/Middleware.sol
+++ b/src/contracts/middleware/Middleware.sol
@@ -14,19 +14,26 @@
 // along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
 pragma solidity 0.8.25;
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
 import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
 import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
 import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
-import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
 
 //**************************************************************************************************
 //                                      SYMBIOTIC
 //**************************************************************************************************
 import {IEntity} from "@symbiotic/interfaces/common/IEntity.sol";
 import {IVault} from "@symbiotic/interfaces/vault/IVault.sol";
+import {IVaultStorage} from "@symbiotic/interfaces/vault/IVaultStorage.sol";
 import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol";
 import {ISlasher} from "@symbiotic/interfaces/slasher/ISlasher.sol";
 import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol";
@@ -36,6 +43,7 @@ import {KeyManager256} from "@symbiotic-middleware/extensions/managers/keys/KeyM
 import {OzAccessControl} from "@symbiotic-middleware/extensions/managers/access/OzAccessControl.sol";
 import {EpochCapture} from "@symbiotic-middleware/extensions/managers/capture-timestamps/EpochCapture.sol";
 import {PauseableEnumerableSet} from "@symbiotic-middleware/libraries/PauseableEnumerableSet.sol";
+
 //**************************************************************************************************
 //                                      SNOWBRIDGE
 //**************************************************************************************************
@@ -46,7 +54,6 @@ import {IODefaultStakerRewardsFactory} from "src/interfaces/rewarder/IODefaultSt
 import {IMiddleware} from "src/interfaces/middleware/IMiddleware.sol";
 import {OSharedVaults} from "src/contracts/extensions/OSharedVaults.sol";
 import {MiddlewareStorage} from "src/contracts/middleware/MiddlewareStorage.sol";
-
 import {QuickSort} from "../libraries/QuickSort.sol";
 
 contract Middleware is
@@ -65,18 +72,24 @@ contract Middleware is
     using Subnetwork for address;
     using Math for uint256;
 
+    modifier notZeroAddress(
+        address address_
+    ) {
+        _checkNotZeroAddress(address_);
+        _;
+    }
+
     /*
      * @notice Constructor for the middleware
      * @param operatorRewards The operator rewards address
      * @param stakerRewardsFactory The staker rewards factory address
      */
-    constructor(address operatorRewards, address stakerRewardsFactory) {
+    constructor(
+        address operatorRewards,
+        address stakerRewardsFactory
+    ) notZeroAddress(operatorRewards) notZeroAddress(stakerRewardsFactory) {
         _disableInitializers();
 
-        if (operatorRewards == address(0) || stakerRewardsFactory == address(0)) {
-            revert Middleware__InvalidAddress();
-        }
-
         i_operatorRewards = operatorRewards;
         i_stakerRewardsFactory = stakerRewardsFactory;
     }
@@ -102,9 +115,11 @@ contract Middleware is
         uint48 slashingWindow,
         address reader
     ) public initializer {
-        if (owner == address(0) || reader == address(0)) {
-            revert Middleware__InvalidAddress();
+        {
+            _checkNotZeroAddress(owner);
+            _checkNotZeroAddress(reader);
         }
+
         if (slashingWindow < epochDuration) {
             revert Middleware__SlashingWindowTooShort();
         }
@@ -126,7 +141,7 @@ contract Middleware is
     /**
      * @inheritdoc OSharedVaults
      */
-    function _beforeRegisterSharedVault(
+    function _afterRegisterSharedVault(
         address sharedVault,
         IODefaultStakerRewards.InitParams memory stakerRewardsParams
     ) internal virtual override {
@@ -134,11 +149,26 @@ contract Middleware is
             IODefaultStakerRewardsFactory(i_stakerRewardsFactory).create(sharedVault, stakerRewardsParams);
 
         IODefaultOperatorRewards(i_operatorRewards).setStakerRewardContract(stakerRewards, sharedVault);
+
+        address collateral = IVault(sharedVault).collateral();
+        _setVaultToCollateral(sharedVault, collateral);
     }
 
-    // TODO: this should probably take into account underlying asset price and return a power which could be either the total value of the stake in usd or a value that makes sense for us
     function stakeToPower(address vault, uint256 stake) public view override returns (uint256 power) {
-        return stake;
+        address collateral = vaultToCollateral(vault);
+        address oracle = collateralToOracle(collateral);
+
+        if (oracle == address(0)) {
+            revert Middleware__NotSupportedCollateral(collateral);
+        }
+        (, int256 price,,,) = AggregatorV3Interface(oracle).latestRoundData();
+        uint8 priceDecimals = AggregatorV3Interface(oracle).decimals();
+        power = stake.mulDiv(uint256(price), 10 ** priceDecimals);
+        // Normalize power to 18 decimals
+        uint8 collateralDecimals = IERC20Metadata(collateral).decimals();
+        if (collateralDecimals != uint8(18)) {
+            power = power.mulDiv(10 ** 18, 10 ** collateralDecimals);
+        }
     }
 
     /**
@@ -153,6 +183,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
+        StorageMiddleware storage $ = _getMiddlewareStorage();
+
+        // Oracle is not checked against zero so this can be used to remove the oracle from a collateral
+
+        $.collateralToOracle[collateral] = oracle;
+        emit CollateralToOracleSet(collateral, oracle);
+    }
+
     /**
      * @inheritdoc IMiddleware
      */
@@ -232,8 +274,11 @@ contract Middleware is
         address[] memory vaults = _activeVaultsAt(epochStartTs, operator);
         // simple pro-rata slasher
         uint256 vaultsLength = vaults.length;
-        for (uint256 i; i < vaultsLength; ++i) {
+        for (uint256 i; i < vaultsLength;) {
             _processVaultSlashing(vaults[i], params);
+            unchecked {
+                ++i;
+            }
         }
     }
 
@@ -323,13 +368,16 @@ contract Middleware is
 
         uint256 valIdx = 0;
         uint256 operatorsLength = operators.length;
-        for (uint256 i; i < operatorsLength; ++i) {
+        for (uint256 i; i < operatorsLength;) {
             address operator = operators[i];
             (uint256 vaultIdx, address[] memory _vaults) = getOperatorVaults(operator, epochStartTs);
 
             if (vaultIdx > 0) {
                 operatorVaultPairs[valIdx++] = OperatorVaultPair(operator, _vaults);
             }
+            unchecked {
+                ++i;
+            }
         }
     }
 
@@ -357,8 +405,11 @@ contract Middleware is
 
         sortedKeys = new bytes32[](validatorSet.length);
         uint256 validatorSetLength = validatorSet.length;
-        for (uint256 i; i < validatorSetLength; ++i) {
+        for (uint256 i; i < validatorSetLength;) {
             sortedKeys[i] = validatorSet[i].key;
+            unchecked {
+                ++i;
+            }
         }
     }
 
@@ -373,7 +424,7 @@ contract Middleware is
         vaults = new address[](operatorVaults.length);
         vaultIdx = 0;
         uint256 operatorVaultsLength = operatorVaults.length;
-        for (uint256 j; j < operatorVaultsLength; ++j) {
+        for (uint256 j; j < operatorVaultsLength;) {
             address _vault = operatorVaults[j];
             // Tanssi will use probably only one subnetwork, so we can skip this loop
             // for (uint96 k = 0; k < _subnetworksLength(); ++k) {
@@ -385,6 +436,9 @@ contract Middleware is
             if (operatorStake > 0) {
                 vaults[vaultIdx++] = _vault;
             }
+            unchecked {
+                ++j;
+            }
         }
         assembly ("memory-safe") {
             mstore(vaults, vaultIdx)
@@ -404,9 +458,12 @@ contract Middleware is
         }
         address[] memory operators = _activeOperatorsAt(epochStartTs);
         uint256 operatorsLength = operators.length;
-        for (uint256 i; i < operatorsLength; ++i) {
+        for (uint256 i; i < operatorsLength;) {
             uint256 operatorStake = _getOperatorPowerAt(epochStartTs, operators[i]);
             totalStake += operatorStake;
+            unchecked {
+                ++i;
+            }
         }
     }
 
@@ -434,20 +491,22 @@ contract Middleware is
     ) public view returns (ValidatorData[] memory validatorSet) {
         uint48 epochStartTs = getEpochStart(epoch);
         address[] memory operators = _activeOperatorsAt(epochStartTs);
-
         validatorSet = new ValidatorData[](operators.length);
+
         uint256 len = 0;
         uint256 operatorsLength = operators.length;
-        for (uint256 i; i < operatorsLength; ++i) {
+        for (uint256 i; i < operatorsLength;) {
             address operator = operators[i];
-
+            unchecked {
+                ++i;
+            }
             bytes32 key = abi.decode(getOperatorKeyAt(operator, epochStartTs), (bytes32));
+
             if (key == bytes32(0)) {
                 continue;
             }
 
             uint256 power = _getOperatorPowerAt(epochStartTs, operator);
-
             validatorSet[len++] = ValidatorData(power, key);
         }
 
@@ -457,13 +516,26 @@ contract Middleware is
         }
     }
 
-    // /**
-    //  * @inheritdoc IMiddleware
-    //  */
+    /**
+     * @inheritdoc IMiddleware
+     */
     function getEpochAtTs(
         uint48 timestamp
     ) public view returns (uint48 epoch) {
         EpochCaptureStorage storage $ = _getEpochCaptureStorage();
         return (timestamp - $.startTimestamp) / $.epochDuration;
     }
+
+    function _setVaultToCollateral(address vault, address collateral) private notZeroAddress(collateral) {
+        StorageMiddleware storage $ = _getMiddlewareStorage();
+        $.vaultToCollateral[vault] = collateral;
+    }
+
+    function _checkNotZeroAddress(
+        address address_
+    ) private pure {
+        if (address_ == address(0)) {
+            revert Middleware__InvalidAddress();
+        }
+    }
 }
diff --git a/src/contracts/middleware/MiddlewareStorage.sol b/src/contracts/middleware/MiddlewareStorage.sol
index a4f17e93..65ce39cd 100644
--- a/src/contracts/middleware/MiddlewareStorage.sol
+++ b/src/contracts/middleware/MiddlewareStorage.sol
@@ -20,6 +20,8 @@ abstract contract MiddlewareStorage {
     /// @custom:storage-location erc7201:tanssi.middleware.MiddlewareStorage.v1.1
     struct StorageMiddleware {
         address gateway;
+        mapping(address collateral => address oracle) collateralToOracle;
+        mapping(address vault => address collateral) vaultToCollateral;
     }
 
     // keccak256(abi.encode(uint256(keccak256("tanssi.middleware.MiddlewareStorage.v1.1")) - 1)) & ~bytes32(uint256(0xff));
@@ -56,4 +58,25 @@ abstract contract MiddlewareStorage {
         StorageMiddleware storage $ = _getMiddlewareStorage();
         return $.gateway;
     }
+
+    function collateralToOracle(
+        address collateral
+    ) public view returns (address) {
+        StorageMiddleware storage $ = _getMiddlewareStorage();
+        return $.collateralToOracle[collateral];
+    }
+
+    function vaultToCollateral(
+        address vault
+    ) public view returns (address) {
+        StorageMiddleware storage $ = _getMiddlewareStorage();
+        return $.vaultToCollateral[vault];
+    }
+
+    function vaultToOracle(
+        address vault
+    ) public view returns (address) {
+        StorageMiddleware storage $ = _getMiddlewareStorage();
+        return $.collateralToOracle[$.vaultToCollateral[vault]];
+    }
 }
diff --git a/src/interfaces/middleware/IMiddleware.sol b/src/interfaces/middleware/IMiddleware.sol
index 2aebd805..356dbb09 100644
--- a/src/interfaces/middleware/IMiddleware.sol
+++ b/src/interfaces/middleware/IMiddleware.sol
@@ -15,6 +15,14 @@
 pragma solidity ^0.8.0;
 
 interface IMiddleware {
+    /**
+     * @notice Emitted when an oracle is set for a collateral.
+     * @dev If the oracle is set to address(0), the collateral will no longer be supported.
+     * @param collateral The collateral address
+     * @param oracle The oracle address
+     */
+    event CollateralToOracleSet(address indexed collateral, address indexed oracle);
+
     // Errors
     error Middleware__CallerNotGateway();
     error Middleware__GatewayNotSet();
@@ -22,6 +30,7 @@ interface IMiddleware {
     error Middleware__InvalidEpoch();
     error Middleware__InvalidAddress();
     error Middleware__InsufficientBalance();
+    error Middleware__NotSupportedCollateral(address collateral);
     error Middleware__SlashingWindowTooShort();
     error Middleware__UnknownSlasherType();
     error Middleware__OperatorNotFound(bytes32 operatorKey, uint48 epoch);
diff --git a/test/fork/Middleware.t.sol b/test/fork/Middleware.t.sol
index 6af994b7..8ed79caa 100644
--- a/test/fork/Middleware.t.sol
+++ b/test/fork/Middleware.t.sol
@@ -32,6 +32,11 @@ import {IDefaultCollateral} from "@symbiotic-collateral/interfaces/defaultCollat
 import {BaseMiddlewareReader} from "@symbiotic-middleware/middleware/BaseMiddlewareReader.sol";
 import {EpochCapture} from "@symbiotic-middleware/extensions/managers/capture-timestamps/EpochCapture.sol";
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
@@ -55,7 +60,7 @@ contract MiddlewareTest is Test {
     uint48 public constant SLASHING_WINDOW = 7 days;
     uint48 public constant VETO_DURATION = 1 days;
     uint256 public constant SLASH_AMOUNT = 30 ether;
-    uint256 public constant OPERATOR_STAKE = 100 ether;
+    uint256 public constant OPERATOR_STAKE = 90 ether;
     uint256 public constant DEFAULT_WITHDRAW_AMOUNT = 30 ether;
     uint256 public constant OPERATOR_INITIAL_BALANCE = 1000 ether;
     uint256 public constant MIN_SLASHING_WINDOW = 1 days;
@@ -67,6 +72,13 @@ contract MiddlewareTest is Test {
     uint128 public constant OPERATOR_NETWORK_LIMIT = 300 ether;
     uint256 public constant TOTAL_NETWORK_SHARES = 3;
     uint256 public constant PARTS_PER_BILLION = 1_000_000_000;
+    uint256 public constant SLASHING_FRACTION = PARTS_PER_BILLION / 10; // 10%
+    uint8 public constant ORACLE_DECIMALS = 3;
+    int256 public constant ORACLE_CONVERSION_TOKEN = 2000;
+
+    uint256 public totalFullRestakePower; // Each operator participates with 100% of all operators stake
+    uint256 public totalPowerVault; // By shares. Each operator participates gets 1/3 of the total power
+    uint256 public totalPowerVaultSlashable; // By shares. Each operator participates gets 1/3 of the total power
 
     uint256 ownerPrivateKey =
         vm.envOr("OWNER_PRIVATE_KEY", uint256(0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6));
@@ -79,6 +91,7 @@ contract MiddlewareTest is Test {
     address public resolver1 = makeAddr("resolver1");
     address public resolver2 = makeAddr("resolver2");
     address public gateway = makeAddr("gateway");
+    address public oracle = makeAddr("oracle");
 
     HelperConfig helperConfig;
 
@@ -118,6 +131,7 @@ contract MiddlewareTest is Test {
         ecosystemEntities.vetoSlasher.setResolver(0, resolver1, hex"");
         ecosystemEntities.vetoSlasher.setResolver(0, resolver2, hex"");
         ecosystemEntities.middleware.setGateway(gateway);
+        ecosystemEntities.middleware.setCollateralToOracle(address(ecosystemEntities.stETH), oracle);
         vm.stopPrank();
 
         _handleDeposits();
@@ -206,15 +220,15 @@ contract MiddlewareTest is Test {
         IOptInService operatorVaultOptInService = IOptInService(operatorVaultOptInServiceAddress);
 
         vm.startPrank(operator);
-        _depositToVault(ecosystemEntities.vault, operator, 100 ether, ecosystemEntities.stETH);
+        _depositToVault(ecosystemEntities.vault, operator, OPERATOR_STAKE, ecosystemEntities.stETH);
         vm.stopPrank();
 
         {
             // Scoped to help with stack depth
             vm.startPrank(operator2);
             operatorVaultOptInService.optIn(address(ecosystemEntities.vaultSlashable));
-            _depositToVault(ecosystemEntities.vaultSlashable, operator2, 100 ether, ecosystemEntities.stETH);
-            _depositToVault(ecosystemEntities.vaultVetoed, operator2, 100 ether, ecosystemEntities.stETH);
+            _depositToVault(ecosystemEntities.vaultSlashable, operator2, OPERATOR_STAKE, ecosystemEntities.stETH);
+            _depositToVault(ecosystemEntities.vaultVetoed, operator2, OPERATOR_STAKE, ecosystemEntities.stETH);
             vm.stopPrank();
         }
 
@@ -223,11 +237,18 @@ contract MiddlewareTest is Test {
             vm.startPrank(operator3);
             operatorVaultOptInService.optIn(address(ecosystemEntities.vault));
             operatorVaultOptInService.optIn(address(ecosystemEntities.vaultVetoed));
-            _depositToVault(ecosystemEntities.vault, operator3, 100 ether, ecosystemEntities.stETH);
-            _depositToVault(ecosystemEntities.vaultSlashable, operator3, 100 ether, ecosystemEntities.stETH);
-            _depositToVault(ecosystemEntities.vaultVetoed, operator3, 100 ether, ecosystemEntities.stETH);
+            _depositToVault(ecosystemEntities.vault, operator3, OPERATOR_STAKE, ecosystemEntities.stETH);
+            _depositToVault(ecosystemEntities.vaultSlashable, operator3, OPERATOR_STAKE, ecosystemEntities.stETH);
+            _depositToVault(ecosystemEntities.vaultVetoed, operator3, OPERATOR_STAKE, ecosystemEntities.stETH);
             vm.stopPrank();
         }
+
+        {
+            totalFullRestakePower = ((OPERATOR_STAKE * 2) * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
+
+            totalPowerVault = (OPERATOR_STAKE * 2 * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
+            totalPowerVaultSlashable = (OPERATOR_STAKE * 2 * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
+        }
     }
 
     function _depositToVault(IVault _vault, address _operator, uint256 _amount, IERC20 collateral) public {
@@ -297,6 +318,16 @@ contract MiddlewareTest is Test {
             tanssi.subnetwork(0), operator3, OPERATOR_SHARE
         );
         vm.stopPrank();
+
+        // Mock the oracle to return the correct conversion token
+        vm.mockCall(
+            oracle,
+            abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector),
+            abi.encode(uint80(0), ORACLE_CONVERSION_TOKEN, uint256(0), uint256(0), uint80(0))
+        );
+        vm.mockCall(
+            oracle, abi.encodeWithSelector(AggregatorV3Interface.decimals.selector), abi.encode(uint8(ORACLE_DECIMALS))
+        );
     }
 
     function _setLimitForNetworkAndOperators(
@@ -316,20 +347,20 @@ contract MiddlewareTest is Test {
     }
 
     /**
-     * @param _operatorStake the total stake of the operator in each vault he is registered
-     * @param _activeStake the active stake of vault's FullRestake delegated
-     * @param _amountSlashed the amount slashed from the operator
-     * @return totalOperatorStake
-     * @return remainingOperatorStake
+     * @param networkRestakePower The total stake of all operator vaults using NetworkRestake delegation
+     * @param fullRestakePower The total stake of all operator vaults using FullRestake delegation
+     * @param amountSlashed The amount slashed from the operator
+     * @return totalOperatorPower
+     * @return operatorPowerFromShares
      */
-    function _calculateTotalOperatorStake(
-        uint256 _operatorStake,
-        uint256 _activeStake,
-        uint256 _amountSlashed
-    ) public pure returns (uint256 totalOperatorStake, uint256 remainingOperatorStake) {
-        remainingOperatorStake =
-            _calculateRemainingStake(OPERATOR_SHARE, TOTAL_NETWORK_SHARES, _operatorStake - _amountSlashed);
-        totalOperatorStake = remainingOperatorStake + _activeStake;
+    function _calculateOperatorPower(
+        uint256 networkRestakePower,
+        uint256 fullRestakePower,
+        uint256 amountSlashed
+    ) public pure returns (uint256 totalOperatorPower, uint256 operatorPowerFromShares) {
+        operatorPowerFromShares =
+            _calculateRemainingStake(OPERATOR_SHARE, TOTAL_NETWORK_SHARES, networkRestakePower - amountSlashed);
+        totalOperatorPower = operatorPowerFromShares + fullRestakePower;
     }
 
     function _calculateRemainingStake(
@@ -422,30 +453,27 @@ contract MiddlewareTest is Test {
         );
     }
 
-    function testSlashingOnOperator2AndVetoingSlash() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
+    function testOperatorPower() public {
+        (, Middleware.ValidatorData[] memory validators, uint256 totalOperator2Stake,, uint256 totalOperator3Stake,) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
         //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
         uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
+        assertEq(activePowerInVetoed, totalFullRestakePower);
 
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
+        assertEq(validators[1].stake, totalOperator2Stake);
+        assertEq(validators[2].stake, totalOperator3Stake);
+    }
 
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
+    function testSlashingOnOperator2AndVetoingSlash() public {
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-        //We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
-        uint256 amountToSlash = 30 ether;
-        uint256 slashingFraction = amountToSlash.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.prank(resolver1);
         ecosystemEntities.vetoSlasher.vetoSlash(0, hex"");
@@ -454,38 +482,23 @@ contract MiddlewareTest is Test {
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
-
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator2AndExecuteSlashOnVetoVault() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
+        // We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
-        uint256 amountToSlash = 30 ether;
-        uint256 slashingFraction = amountToSlash.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
         vm.prank(gateway);
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + VETO_DURATION);
         vm.prank(address(ecosystemEntities.middleware));
@@ -494,175 +507,115 @@ contract MiddlewareTest is Test {
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
+        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
 
+        (uint256 totalOperator2StakeAfter,) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndVetoingSlash() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 = (SLASH_AMOUNT * remainingOperator3Stake) / totalOperator3Stake;
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * (powerFromSharesOperator3 / 2)) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR3_KEY, SLASHING_FRACTION);
 
         vm.prank(resolver1);
         ecosystemEntities.vetoSlasher.vetoSlash(0, hex"");
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable3);
-
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable3);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndExecuteSlashOnVetoVault() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 =
-            (SLASH_AMOUNT * remainingOperator3Stake) / (totalOperator3Stake + remainingOperator3Stake);
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-
-        uint256 slashingFraction =
-            slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake + remainingOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator3 / 2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR3_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR3_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + VETO_DURATION);
         vm.prank(address(ecosystemEntities.middleware));
         ecosystemEntities.vetoSlasher.executeSlash(0, hex"");
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable3);
+        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
+
+        (uint256 totalOperator2StakeAfter, uint256 powerFromSharesOperator2After) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+        (uint256 totalOperator3StakeAfter, uint256 powerFromSharesOperator3After) =
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
 
-        (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable3);
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingAndPausingVault() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,,) = _prepareSlashingTest();
 
         vm.prank(owner);
         ecosystemEntities.middleware.pauseSharedVault(vaultAddresses.vaultSlashable);
 
         vm.prank(gateway);
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
-        assertEq(validators[1].stake, OPERATOR_STAKE * 2);
-        assertEq(validators[2].stake, OPERATOR_STAKE * 2 + remainingOperator2Stake);
+        (uint256 totalOperator2StakeAfter,) = _calculateOperatorPower(0, totalFullRestakePower, 0);
+        (uint256 totalOperator3StakeAfter,) = _calculateOperatorPower(totalPowerVault, totalFullRestakePower, 0);
+
+        assertEq(validators[1].stake, totalOperator2StakeAfter);
+        assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingAndPausingOperator() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
         vm.prank(owner);
         ecosystemEntities.middleware.pauseOperator(operator2);
 
+        // We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
+
         vm.prank(gateway);
         //! Why this slash should anyway go through if operator was paused? Shouldn't it revert?
-        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        ecosystemEntities.middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+        // Index is 1 instead of 2 because operator2 was paused
         assertEq(validators[1].stake, totalOperator3StakeAfter);
     }
 
@@ -762,4 +715,26 @@ contract MiddlewareTest is Test {
 
         middlewareImpl = new Middleware(operatorRewardsAddress, stakerRewardsFactoryAddress);
     }
+
+    function _prepareSlashingTest()
+        public
+        returns (
+            uint48 currentEpoch,
+            Middleware.ValidatorData[] memory validators,
+            uint256 totalOperator2Stake,
+            uint256 powerFromSharesOperator2,
+            uint256 totalOperator3Stake,
+            uint256 powerFromSharesOperator3
+        )
+    {
+        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
+        currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
+
+        validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
+
+        (totalOperator2Stake, powerFromSharesOperator2) =
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, 0);
+        (totalOperator3Stake, powerFromSharesOperator3) =
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, 0);
+    }
 }
diff --git a/test/integration/Middleware.t.sol b/test/integration/Middleware.t.sol
index 5a186a64..cfdaa6cb 100644
--- a/test/integration/Middleware.t.sol
+++ b/test/integration/Middleware.t.sol
@@ -51,12 +51,18 @@ import {PauseableEnumerableSet} from "@symbiotic-middleware/libraries/PauseableE
 import {VaultManager} from "@symbiotic-middleware/managers/VaultManager.sol";
 import {OperatorManager} from "@symbiotic-middleware/managers/OperatorManager.sol";
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {MockV3Aggregator} from "@chainlink/tests/MockV3Aggregator.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
 import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
 import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
 import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
+import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
 //**************************************************************************************************
 //                                      SNOWBRIDGE
@@ -97,16 +103,42 @@ contract MiddlewareTest is Test {
     uint48 public constant SLASHING_WINDOW = 7 days;
     uint48 public constant VETO_DURATION = 1 days;
     uint256 public constant SLASH_AMOUNT = 30 ether;
-    uint256 public constant OPERATOR_STAKE = 100 ether;
+    uint256 public constant OPERATOR_STAKE_ST_ETH = 90 ether;
+    uint256 public constant OPERATOR_STAKE_R_ETH = 90 ether;
+    uint256 public constant OPERATOR_STAKE_BTC = 9 ether;
     uint256 public constant DEFAULT_WITHDRAW_AMOUNT = 30 ether;
     uint256 public constant OPERATOR_INITIAL_BALANCE = 1000 ether;
     uint256 public constant MIN_SLASHING_WINDOW = 1 days;
     bytes32 public constant OPERATOR_KEY = bytes32(uint256(1));
     bytes32 public constant OPERATOR2_KEY = bytes32(uint256(2));
     bytes32 public constant OPERATOR3_KEY = bytes32(uint256(3));
+    bytes32 public constant OPERATOR4_KEY = bytes32(uint256(4));
+    bytes32 public constant OPERATOR5_KEY = bytes32(uint256(5));
+
     uint256 public constant OPERATOR_SHARE = 1;
     uint256 public constant TOTAL_NETWORK_SHARES = 3;
     uint256 public constant PARTS_PER_BILLION = 1_000_000_000;
+    uint256 public constant SLASHING_FRACTION = PARTS_PER_BILLION / 10; // 10%
+
+    uint8 public constant ORACLE_DECIMALS = 18;
+    int256 public constant ORACLE_CONVERSION_ST_ETH = 3000 ether;
+    int256 public constant ORACLE_CONVERSION_R_ETH = 3000 ether;
+    int256 public constant ORACLE_CONVERSION_W_BTC = 90_000 ether;
+
+    uint8 public constant USDC_ORACLE_DECIMALS = 8; // USDC_ORACLE_DECIMALS
+    uint8 public constant USDC_TOKEN_DECIMALS = 6; // USDC_TOKEN_DECIMALS
+    uint8 public constant USDT_ORACLE_DECIMALS = 18; // USDT_ORACLE_DECIMALS
+    uint8 public constant USDT_TOKEN_DECIMALS = 18; // USDT_TOKEN_DECIMALS
+
+    // It's Both are staking 150 USD worth in total
+    uint256 public constant OPERATOR_4_STAKE_USDC = 90 * 10 ** USDC_TOKEN_DECIMALS;
+    uint256 public constant OPERATOR_4_STAKE_USDT = 60 * 10 ** USDT_TOKEN_DECIMALS;
+    uint256 public constant OPERATOR_5_STAKE_USDC = 60 * 10 ** USDC_TOKEN_DECIMALS;
+    uint256 public constant OPERATOR_5_STAKE_USDT = 90 * 10 ** USDT_TOKEN_DECIMALS;
+
+    uint256 public totalFullRestakePower; // Each operator participates with 100% of all operators stake
+    uint256 public totalPowerVault; // By shares. Each operator participates gets 1/3 of the total power
+    uint256 public totalPowerVaultSlashable; // By shares. Each operator participates gets 1/3 of the total power
 
     struct VaultAddresses {
         address vault;
@@ -156,10 +188,10 @@ contract MiddlewareTest is Test {
     address public owner = vm.addr(ownerPrivateKey);
 
     address public operator = makeAddr("operator");
-
     address public operator2 = makeAddr("operator2");
-
     address public operator3 = makeAddr("operator3");
+    address public operator4 = makeAddr("operator4");
+    address public operator5 = makeAddr("operator5");
 
     address public resolver1 = makeAddr("resolver1");
     address public resolver2 = makeAddr("resolver2");
@@ -179,11 +211,12 @@ contract MiddlewareTest is Test {
     // Scripts
     DeployVault deployVault;
     DeployRewards deployRewards;
+    DeployCollateral deployCollateral;
     ODefaultOperatorRewards operatorRewards;
     ODefaultStakerRewardsFactory stakerRewardsFactory;
 
     function setUp() public {
-        DeployCollateral deployCollateral = new DeployCollateral();
+        deployCollateral = new DeployCollateral();
 
         vm.startPrank(owner);
         address stETHAddress = deployCollateral.deployCollateral("stETH");
@@ -197,6 +230,10 @@ contract MiddlewareTest is Test {
         wBTC.mint(owner, 1_000_000 ether);
         vm.stopPrank();
 
+        address stEthOracle = _deployOracle(ORACLE_DECIMALS, ORACLE_CONVERSION_ST_ETH);
+        address rEthOracle = _deployOracle(ORACLE_DECIMALS, ORACLE_CONVERSION_R_ETH);
+        address wBtcOracle = _deployOracle(ORACLE_DECIMALS, ORACLE_CONVERSION_W_BTC);
+
         deployVault = new DeployVault();
         deployRewards = new DeployRewards(true);
         DeploySymbiotic deploySymbiotic = new DeploySymbiotic();
@@ -241,6 +278,9 @@ contract MiddlewareTest is Test {
         middleware = _deployMiddlewareWithProxy(tanssi, owner, operatorRewardsAddress, stakerRewardsFactoryAddress);
         _createGateway();
         middleware.setGateway(address(gateway));
+        middleware.setCollateralToOracle(address(stETH), stEthOracle);
+        middleware.setCollateralToOracle(address(rETH), rEthOracle);
+        middleware.setCollateralToOracle(address(wBTC), wBtcOracle);
 
         vetoSlasher = VetoSlasher(vaultAddresses.slasherVetoed);
 
@@ -255,9 +295,12 @@ contract MiddlewareTest is Test {
         vaults.push(vaultSlashable);
         vaults.push(vaultVetoed);
 
-        _registerOperator(operator, tanssi, address(vault));
-        _registerOperator(operator3, tanssi, address(vaultSlashable));
-        _registerOperator(operator2, tanssi, address(vaultVetoed));
+        _registerOperatorAndOptIn(operator, tanssi, address(vault), true);
+        _registerOperatorAndOptIn(operator2, tanssi, address(vaultVetoed), true);
+        _registerOperatorAndOptIn(operator2, tanssi, address(vaultSlashable), false);
+        _registerOperatorAndOptIn(operator3, tanssi, address(vault), true);
+        _registerOperatorAndOptIn(operator3, tanssi, address(vaultVetoed), false);
+        _registerOperatorAndOptIn(operator3, tanssi, address(vaultSlashable), false);
 
         _registerEntitiesToMiddleware(owner);
         _setOperatorsNetworkShares(tanssi);
@@ -265,21 +308,22 @@ contract MiddlewareTest is Test {
         _setLimitForNetworkAndOperators(tanssi);
 
         vm.startPrank(operator);
-        _depositToVault(vault, operator, 100 ether, stETH);
+        _depositToVault(vault, operator, OPERATOR_STAKE_ST_ETH, stETH);
 
         vm.startPrank(operator2);
-        operatorVaultOptInService.optIn(address(vaultSlashable));
-        _depositToVault(vaultSlashable, operator2, 100 ether, rETH);
-        _depositToVault(vaultVetoed, operator2, 100 ether, wBTC);
+        _depositToVault(vaultSlashable, operator2, OPERATOR_STAKE_R_ETH, rETH);
+        _depositToVault(vaultVetoed, operator2, OPERATOR_STAKE_BTC, wBTC);
         vm.stopPrank();
 
         vm.startPrank(operator3);
-        operatorVaultOptInService.optIn(address(vault));
-        operatorVaultOptInService.optIn(address(vaultVetoed));
-        _depositToVault(vault, operator3, 100 ether, stETH);
-        _depositToVault(vaultSlashable, operator3, 100 ether, rETH);
-        _depositToVault(vaultVetoed, operator3, 100 ether, wBTC);
+        _depositToVault(vault, operator3, OPERATOR_STAKE_ST_ETH, stETH);
+        _depositToVault(vaultSlashable, operator3, OPERATOR_STAKE_R_ETH, rETH);
+        _depositToVault(vaultVetoed, operator3, OPERATOR_STAKE_BTC, wBTC);
 
+        totalFullRestakePower = ((OPERATOR_STAKE_BTC * 2) * uint256(ORACLE_CONVERSION_W_BTC)) / 10 ** ORACLE_DECIMALS;
+
+        totalPowerVault = (OPERATOR_STAKE_ST_ETH * 2 * uint256(ORACLE_CONVERSION_ST_ETH)) / 10 ** ORACLE_DECIMALS;
+        totalPowerVaultSlashable = (OPERATOR_STAKE_R_ETH * 2 * uint256(ORACLE_CONVERSION_R_ETH)) / 10 ** ORACLE_DECIMALS;
         vm.stopPrank();
     }
 
@@ -362,11 +406,13 @@ contract MiddlewareTest is Test {
         vm.stopPrank();
     }
 
-    function _registerOperator(address _operator, address _network, address _vault) public {
+    function _registerOperatorAndOptIn(address _operator, address _network, address _vault, bool firstTime) public {
         vm.startPrank(_operator);
-        operatorRegistry.registerOperator();
+        if (firstTime) {
+            operatorRegistry.registerOperator();
+            operatorNetworkOptInService.optIn(_network);
+        }
         operatorVaultOptInService.optIn(address(_vault));
-        operatorNetworkOptInService.optIn(_network);
         vm.stopPrank();
     }
 
@@ -422,20 +468,20 @@ contract MiddlewareTest is Test {
     }
 
     /**
-     * @param _operatorStake the total stake of the operator in each vault he is registered
-     * @param _activeStake the active stake of vault's FullRestake delegated
-     * @param _amountSlashed the amount slashed from the operator
-     * @return totalOperatorStake
-     * @return remainingOperatorStake
+     * @param networkRestakePower The total stake of all operator vaults using NetworkRestake delegation
+     * @param fullRestakePower The total stake of all operator vaults using FullRestake delegation
+     * @param amountSlashed The amount slashed from the operator
+     * @return totalOperatorPower
+     * @return operatorPowerFromShares
      */
-    function _calculateTotalOperatorStake(
-        uint256 _operatorStake,
-        uint256 _activeStake,
-        uint256 _amountSlashed
-    ) public pure returns (uint256 totalOperatorStake, uint256 remainingOperatorStake) {
-        remainingOperatorStake =
-            _calculateRemainingStake(OPERATOR_SHARE, TOTAL_NETWORK_SHARES, _operatorStake - _amountSlashed);
-        totalOperatorStake = remainingOperatorStake + _activeStake;
+    function _calculateOperatorPower(
+        uint256 networkRestakePower,
+        uint256 fullRestakePower,
+        uint256 amountSlashed
+    ) public pure returns (uint256 totalOperatorPower, uint256 operatorPowerFromShares) {
+        operatorPowerFromShares =
+            _calculateRemainingStake(OPERATOR_SHARE, TOTAL_NETWORK_SHARES, networkRestakePower - amountSlashed);
+        totalOperatorPower = operatorPowerFromShares + fullRestakePower;
     }
 
     function _calculateRemainingStake(
@@ -513,36 +559,30 @@ contract MiddlewareTest is Test {
         currentEpoch = vaultSlashable.currentEpoch();
         vm.prank(operator2);
         vaultSlashable.claim(operator2, currentEpoch - 1);
-        assertEq(rETH.balanceOf(operator2), OPERATOR_INITIAL_BALANCE - OPERATOR_STAKE + DEFAULT_WITHDRAW_AMOUNT);
+        assertEq(rETH.balanceOf(operator2), OPERATOR_INITIAL_BALANCE - OPERATOR_STAKE_ST_ETH + DEFAULT_WITHDRAW_AMOUNT);
     }
 
-    function testSlashingOnOperator2AndVetoingSlash() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
+    function testOperatorPower() public {
+        (, Middleware.ValidatorData[] memory validators, uint256 totalOperator2Stake,, uint256 totalOperator3Stake,) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
         //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
         uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_W_BTC)) / 10 ** ORACLE_DECIMALS;
+        assertEq(activePowerInVetoed, totalFullRestakePower);
 
         assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
+        assertEq(validators[2].stake, totalOperator3Stake);
+    }
 
-        //We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
+    function testSlashingOnOperator2AndVetoingSlash() public {
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.prank(resolver1);
         vetoSlasher.vetoSlash(0, hex"");
@@ -551,10 +591,10 @@ contract MiddlewareTest is Test {
         validators = middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
-
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
@@ -567,17 +607,9 @@ contract MiddlewareTest is Test {
         // We go directly to epochStart as it 100% ensure that the epoch is started and thus the slashing is invalid
         vm.warp(epochStartTs);
 
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
-
         vm.prank(gateway);
         vm.expectRevert(IVetoSlasher.InvalidCaptureTimestamp.selector);
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
         vm.stopPrank();
     }
 
@@ -589,7 +621,7 @@ contract MiddlewareTest is Test {
         // We go directly to epochStart as it 100% ensure that the epoch is started and thus the slashing is invalid
         vm.warp(epochStartTs);
 
-        // We want to slash 30 ether, so we need to calculate what percentage
+        // 150%
         uint256 slashingFraction = (3 * PARTS_PER_BILLION) / 2;
 
         vm.prank(gateway);
@@ -602,32 +634,14 @@ contract MiddlewareTest is Test {
     }
 
     function testSlashingOnOperator2AndExecuteSlashOnVetoVault() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        // We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + VETO_DURATION);
         vm.prank(address(middleware));
@@ -636,210 +650,130 @@ contract MiddlewareTest is Test {
         uint48 newEpoch = middleware.getCurrentEpoch();
         validators = middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
+        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_W_BTC)) / 10 ** ORACLE_DECIMALS;
 
+        (uint256 totalOperator2StakeAfter,) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndVetoingSlash() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 = (SLASH_AMOUNT * remainingOperator3Stake) / totalOperator3Stake;
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * (powerFromSharesOperator3 / 2)) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        middleware.slash(currentEpoch, OPERATOR3_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR3_KEY, SLASHING_FRACTION);
 
         vm.prank(resolver1);
         vetoSlasher.vetoSlash(0, hex"");
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = middleware.getCurrentEpoch();
         validators = middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable3);
-
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable3);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndExecuteSlashOnVetoVault() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 =
-            (SLASH_AMOUNT * remainingOperator3Stake) / (totalOperator3Stake + remainingOperator3Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-
-        uint256 slashingFraction =
-            slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake + remainingOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator3 / 2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
-        middleware.slash(currentEpoch, OPERATOR3_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR3_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + VETO_DURATION);
         vm.prank(address(middleware));
         vetoSlasher.executeSlash(0, hex"");
+
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = middleware.getCurrentEpoch();
         validators = middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable3);
+        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_W_BTC)) / 10 ** ORACLE_DECIMALS;
 
-        (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable3);
+        (uint256 totalOperator2StakeAfter, uint256 powerFromSharesOperator2After) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+        (uint256 totalOperator3StakeAfter, uint256 powerFromSharesOperator3After) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+        // The first vault is not Slashable, so we calculate the power with no slashing
+        (uint256 totalOperator3StakeFirstVault,) = _calculateOperatorPower(totalPowerVault, 0, 0);
 
         assertEq(validators[1].stake, totalOperator2StakeAfter);
-        assertEq(validators[2].stake, totalOperator3StakeAfter);
+        assertEq(validators[2].stake, totalOperator3StakeAfter + totalOperator3StakeFirstVault);
     }
 
     function testSlashingAndPausingVault() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,,) = _prepareSlashingTest();
 
         vm.prank(owner);
         middleware.pauseSharedVault(vaultAddresses.vaultSlashable);
 
         vm.prank(gateway);
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = middleware.getCurrentEpoch();
         validators = middleware.getValidatorSet(newEpoch);
 
-        assertEq(validators[1].stake, OPERATOR_STAKE * 2);
-        assertEq(validators[2].stake, OPERATOR_STAKE * 2 + remainingOperator2Stake);
+        (uint256 totalOperator2StakeAfter,) = _calculateOperatorPower(0, totalFullRestakePower, 0);
+        (uint256 totalOperator3StakeAfter,) = _calculateOperatorPower(totalPowerVault, totalFullRestakePower, 0);
+
+        assertEq(validators[1].stake, totalOperator2StakeAfter);
+        assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingAndPausingOperator() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
         vm.prank(owner);
         middleware.pauseOperator(operator2);
 
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
+        // We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
         vm.prank(gateway);
         //! Why this slash should anyway go through if operator was paused? Shouldn't it revert?
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.warp(block.timestamp + SLASHING_WINDOW + 1);
         uint48 newEpoch = middleware.getCurrentEpoch();
         validators = middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+        // Index is 1 instead of 2 because operator2 was paused
         assertEq(validators[1].stake, totalOperator3StakeAfter);
     }
 
     function testSlashEvenIfWeChangeOperatorKey() public {
-        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = middleware.getCurrentEpoch();
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,, uint256 powerFromSharesOperator2,,) =
+            _prepareSlashingTest();
 
-        Middleware.ValidatorData[] memory validators = middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake, uint256 remainingOperator2Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
-
-        //We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable = (SLASH_AMOUNT * remainingOperator2Stake) / totalOperator2Stake;
+        // We calculate the amount slashable for only the operator2 since it's the only one that should be slashed. As a side effect operator3 will be slashed too since it's taking part in a NetworkRestake delegator based vault
+        uint256 slashingPower = (SLASHING_FRACTION * powerFromSharesOperator2) / PARTS_PER_BILLION;
 
         // Everything below should be call with the owner key
         vm.startPrank(owner);
 
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator2Stake);
-
         // Before slashing, we will change the operator2 key to something else, and prove we can still slash
         // This is because operator keys work with timestamps and old keys are maintained, not removed
         // Therefore we will always be able to slash
@@ -847,7 +781,7 @@ contract MiddlewareTest is Test {
         middleware.updateOperatorKey(operator2, abi.encode(differentOperatorKey));
 
         vm.startPrank(gateway);
-        middleware.slash(currentEpoch, OPERATOR2_KEY, slashingFraction);
+        middleware.slash(currentEpoch, OPERATOR2_KEY, SLASHING_FRACTION);
 
         vm.startPrank(resolver1);
         vetoSlasher.vetoSlash(0, hex"");
@@ -856,18 +790,18 @@ contract MiddlewareTest is Test {
         validators = middleware.getValidatorSet(newEpoch);
 
         (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
-
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testOperatorsOnlyInTanssiNetwork() public {
-        address operator4 = makeAddr("operator4");
+        address operatorX = makeAddr("operatorX");
         address network2 = makeAddr("network2");
-        bytes32 OPERATOR4_KEY = bytes32(uint256(4));
+        bytes32 OPERATORX_KEY = bytes32(uint256(4));
 
         //Middleware 2 Deployment
         vm.startPrank(network2);
@@ -875,14 +809,14 @@ contract MiddlewareTest is Test {
         INetworkRestakeDelegator(vaultAddresses.delegator).setMaxNetworkLimit(0, 1000 ether);
 
         vm.startPrank(owner);
-        stETH.transfer(operator4, OPERATOR_INITIAL_BALANCE);
+        stETH.transfer(operatorX, OPERATOR_INITIAL_BALANCE);
         INetworkRestakeDelegator(vaultAddresses.delegator).setOperatorNetworkShares(
-            network2.subnetwork(0), operator4, OPERATOR_SHARE
+            network2.subnetwork(0), operatorX, OPERATOR_SHARE
         );
         INetworkRestakeDelegator(vaultAddresses.delegator).setNetworkLimit(network2.subnetwork(0), 300 ether);
 
-        // Operator4 registration and network configuration
-        _registerOperator(operator4, network2, address(vault));
+        // OperatorX registration and network configuration
+        _registerOperatorAndOptIn(operatorX, network2, address(vault), true);
 
         address operatorRewardsAddress2 =
             deployRewards.deployOperatorRewardsContract(network2, address(networkMiddlewareService), 5000, owner);
@@ -897,7 +831,7 @@ contract MiddlewareTest is Test {
             adminFeeSetRoleHolder: network2
         });
         middleware2.registerSharedVault(address(vault), stakerRewardsParams);
-        middleware2.registerOperator(operator4, abi.encode(OPERATOR4_KEY), address(0));
+        middleware2.registerOperator(operatorX, abi.encode(OPERATORX_KEY), address(0));
 
         vm.stopPrank();
 
@@ -906,16 +840,108 @@ contract MiddlewareTest is Test {
         Middleware.OperatorVaultPair[] memory operator2VaultPairs =
             middleware2.getOperatorVaultPairs(middleware2CurrentEpoch);
         assertEq(operator2VaultPairs.length, 1);
-        assertEq(operator2VaultPairs[0].operator, operator4);
+        assertEq(operator2VaultPairs[0].operator, operatorX);
         assertEq(operator2VaultPairs[0].vaults.length, 1);
         uint48 middlewareCurrentEpoch = middleware.getCurrentEpoch();
         Middleware.OperatorVaultPair[] memory operatorVaultPairs =
             middleware.getOperatorVaultPairs(middlewareCurrentEpoch);
         for (uint256 i = 0; i < operatorVaultPairs.length; i++) {
-            assert(operatorVaultPairs[i].operator != operator4);
+            assert(operatorVaultPairs[i].operator != operatorX);
         }
     }
 
+    function testCollateralsWithDifferentDecimals() public {
+        vm.startPrank(owner);
+
+        Token usdc = Token(deployCollateral.deployCollateral("usdc", USDC_TOKEN_DECIMALS));
+        Token usdt = Token(deployCollateral.deployCollateral("usdt", USDT_TOKEN_DECIMALS));
+
+        usdc.mint(operator4, 1000 * 10 ** USDC_TOKEN_DECIMALS);
+        usdc.mint(operator5, 1000 * 10 ** USDC_TOKEN_DECIMALS);
+        usdt.mint(operator4, 1000 * 10 ** USDT_TOKEN_DECIMALS);
+        usdt.mint(operator5, 1000 * 10 ** USDT_TOKEN_DECIMALS);
+
+        address usdcOracle = _deployOracle(USDC_ORACLE_DECIMALS, int256(1 * 10 ** USDC_ORACLE_DECIMALS));
+        address usdtOracle = _deployOracle(USDT_ORACLE_DECIMALS, int256(1 * 10 ** USDT_ORACLE_DECIMALS));
+
+        DeployVault.CreateVaultBaseParams memory params = DeployVault.CreateVaultBaseParams({
+            epochDuration: VAULT_EPOCH_DURATION,
+            depositWhitelist: false,
+            depositLimit: 0,
+            delegatorIndex: DeployVault.DelegatorIndex.NETWORK_RESTAKE,
+            shouldBroadcast: false,
+            vaultConfigurator: address(vaultConfigurator),
+            collateral: address(usdc),
+            owner: tanssi
+        });
+
+        (address vaultUsdc, address vaultDelegatorUsdc,) = deployVault.createBaseVault(params);
+
+        params.collateral = address(usdt);
+        (address vaultUsdt, address vaultDelegatorUsdt,) = deployVault.createBaseVault(params);
+
+        middleware.setCollateralToOracle(address(usdc), usdcOracle);
+        middleware.setCollateralToOracle(address(usdt), usdtOracle);
+
+        _registerOperatorAndOptIn(operator4, tanssi, address(vaultUsdc), true);
+        _registerOperatorAndOptIn(operator4, tanssi, address(vaultUsdt), false);
+
+        _registerOperatorAndOptIn(operator5, tanssi, address(vaultUsdc), true);
+        _registerOperatorAndOptIn(operator5, tanssi, address(vaultUsdt), false);
+
+        vm.startPrank(owner);
+
+        IODefaultStakerRewards.InitParams memory stakerRewardsParams = IODefaultStakerRewards.InitParams({
+            adminFee: 0,
+            defaultAdminRoleHolder: tanssi,
+            adminFeeClaimRoleHolder: tanssi,
+            adminFeeSetRoleHolder: tanssi
+        });
+        middleware.registerSharedVault(vaultUsdc, stakerRewardsParams);
+        middleware.registerSharedVault(vaultUsdt, stakerRewardsParams);
+
+        middleware.registerOperator(operator4, abi.encode(OPERATOR4_KEY), address(0));
+        middleware.registerOperator(operator5, abi.encode(OPERATOR5_KEY), address(0));
+
+        vm.startPrank(tanssi);
+
+        INetworkRestakeDelegator(vaultDelegatorUsdc).setOperatorNetworkShares(tanssi.subnetwork(0), operator4, 1);
+        INetworkRestakeDelegator(vaultDelegatorUsdc).setOperatorNetworkShares(tanssi.subnetwork(0), operator5, 1);
+
+        INetworkRestakeDelegator(vaultDelegatorUsdt).setOperatorNetworkShares(tanssi.subnetwork(0), operator4, 1);
+        INetworkRestakeDelegator(vaultDelegatorUsdt).setOperatorNetworkShares(tanssi.subnetwork(0), operator5, 1);
+
+        INetworkRestakeDelegator(vaultDelegatorUsdc).setMaxNetworkLimit(0, 1000 * 10 ** USDC_ORACLE_DECIMALS);
+        INetworkRestakeDelegator(vaultDelegatorUsdt).setMaxNetworkLimit(0, 1000 * 10 ** USDT_ORACLE_DECIMALS);
+
+        INetworkRestakeDelegator(vaultDelegatorUsdc).setNetworkLimit(
+            tanssi.subnetwork(0), 1000 * 10 ** USDC_ORACLE_DECIMALS
+        );
+        INetworkRestakeDelegator(vaultDelegatorUsdt).setNetworkLimit(
+            tanssi.subnetwork(0), 1000 * 10 ** USDT_ORACLE_DECIMALS
+        );
+
+        vm.startPrank(operator4);
+        _depositToVault(Vault(vaultUsdc), operator4, OPERATOR_4_STAKE_USDC, usdc);
+        _depositToVault(Vault(vaultUsdt), operator4, OPERATOR_4_STAKE_USDT, usdt);
+
+        vm.startPrank(operator5);
+        _depositToVault(Vault(vaultUsdc), operator5, OPERATOR_5_STAKE_USDC, usdc);
+        _depositToVault(Vault(vaultUsdt), operator5, OPERATOR_5_STAKE_USDT, usdt);
+
+        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
+        uint48 currentEpoch = middleware.getCurrentEpoch();
+        Middleware.ValidatorData[] memory validators = _validatorSet(currentEpoch);
+
+        // Total deposit is 300 USD, it should be normalized to 18 decimals
+        uint256 totalPowerByShares = 300 ether;
+        // Only 2 operators participate in the USD vaults, so each has half of the power.
+        uint256 totalPowerOperator = totalPowerByShares / 2;
+
+        assertEq(validators[3].stake, totalPowerOperator);
+        assertEq(validators[4].stake, totalPowerOperator);
+    }
+
     function _createGateway() internal returns (address) {
         ParaID bridgeHubParaID = ParaID.wrap(1013);
         bytes32 bridgeHubAgentID = 0x03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314;
@@ -1013,7 +1039,7 @@ contract MiddlewareTest is Test {
             } else {
                 stETH.transfer(_operator, 1 ether);
             }
-            _registerOperator(_operator, tanssi, address(_vault));
+            _registerOperatorAndOptIn(_operator, tanssi, address(_vault), true);
             vm.startPrank(_operator);
             uint256 depositAmount = 0.001 ether * (i + 1);
             _depositToVault(Vault(_vault), _operator, 0.001 ether * (i + 1), token);
@@ -1276,4 +1302,31 @@ contract MiddlewareTest is Test {
 
         return testVaultAddresses;
     }
+
+    function _prepareSlashingTest()
+        public
+        returns (
+            uint48 currentEpoch,
+            Middleware.ValidatorData[] memory validators,
+            uint256 totalOperator2Stake,
+            uint256 powerFromSharesOperator2,
+            uint256 totalOperator3Stake,
+            uint256 powerFromSharesOperator3
+        )
+    {
+        vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
+        currentEpoch = middleware.getCurrentEpoch();
+
+        validators = middleware.getValidatorSet(currentEpoch);
+
+        (totalOperator2Stake, powerFromSharesOperator2) =
+            _calculateOperatorPower(totalPowerVaultSlashable, totalFullRestakePower, 0);
+        (totalOperator3Stake, powerFromSharesOperator3) =
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, totalFullRestakePower, 0);
+    }
+
+    function _deployOracle(uint8 decimals, int256 answer) public returns (address) {
+        MockV3Aggregator oracle = new MockV3Aggregator(decimals, answer);
+        return address(oracle);
+    }
 }
diff --git a/test/mocks/Token.sol b/test/mocks/Token.sol
index 6a5c8a01..266efe7c 100644
--- a/test/mocks/Token.sol
+++ b/test/mocks/Token.sol
@@ -17,13 +17,18 @@ pragma solidity 0.8.25;
 import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
 contract Token is ERC20 {
-    constructor(
-        string memory name_
-    ) ERC20(name_, "") {
+    uint8 private immutable _decimals;
+
+    constructor(string memory name_, uint8 decimals_) ERC20(name_, "") {
+        _decimals = decimals_;
         _mint(msg.sender, 1_000_000 * 10 ** decimals());
     }
 
     function mint(address to, uint256 amount) external {
         _mint(to, amount);
     }
+
+    function decimals() public view override returns (uint8) {
+        return _decimals;
+    }
 }
diff --git a/test/mocks/symbiotic/SharedVaultMock.sol b/test/mocks/symbiotic/SharedVaultMock.sol
index 92c11ddb..eb09e91c 100644
--- a/test/mocks/symbiotic/SharedVaultMock.sol
+++ b/test/mocks/symbiotic/SharedVaultMock.sol
@@ -24,10 +24,10 @@ contract SharedVaultMock is OSharedVaults, KeyManager256, EpochCapture {
 
     function stakeToPower(address, uint256) public pure override returns (uint256 power) {}
 
-    function callBeforeRegisterHook(
+    function callAfterRegisterHook(
         address sharedVault,
         IODefaultStakerRewards.InitParams memory stakerRewardsParams
     ) public checkAccess {
-        _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
+        _afterRegisterSharedVault(sharedVault, stakerRewardsParams);
     }
 }
diff --git a/test/mocks/symbiotic/VaultMock.sol b/test/mocks/symbiotic/VaultMock.sol
index e8fc9280..ec5bca5e 100644
--- a/test/mocks/symbiotic/VaultMock.sol
+++ b/test/mocks/symbiotic/VaultMock.sol
@@ -30,8 +30,12 @@ contract VaultMock is VaultStorage, MigratableEntity, IVault {
     constructor(
         address delegatorFactory,
         address slasherFactory,
-        address vaultFactory
-    ) VaultStorage(delegatorFactory, slasherFactory) MigratableEntity(vaultFactory) {}
+        address vaultFactory,
+        address _collateral
+    ) VaultStorage(delegatorFactory, slasherFactory) MigratableEntity(vaultFactory) {
+        collateral = _collateral;
+        epochDuration = 10 days;
+    }
 
     function test() public {}
 
diff --git a/test/unit/Deploy.t.sol b/test/unit/Deploy.t.sol
index a9ef79f0..e5bf3b80 100644
--- a/test/unit/Deploy.t.sol
+++ b/test/unit/Deploy.t.sol
@@ -503,46 +503,6 @@ contract DeployTest is Test {
         vm.stopPrank();
     }
 
-    function testSetDelegatorConfigsWithSepoliaChain() public {
-        vm.chainId(11_155_111);
-        helperConfig = new HelperConfig();
-
-        deployTanssiEcosystem.deployTanssiEcosystem(helperConfig);
-        vm.startPrank(tanssi);
-
-        (, address delegator,,, address delegatorSlashable,,, address delegatorVetoed,) =
-            deployTanssiEcosystem.vaultAddresses();
-
-        assertEq(
-            INetworkRestakeDelegator(delegator).maxNetworkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-        assertEq(
-            INetworkRestakeDelegator(delegatorSlashable).maxNetworkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-        assertEq(
-            INetworkRestakeDelegator(delegatorVetoed).maxNetworkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-
-        // Verify subnet network limits
-        assertEq(
-            INetworkRestakeDelegator(delegator).networkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-        assertEq(
-            INetworkRestakeDelegator(delegatorSlashable).networkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-
-        assertEq(
-            INetworkRestakeDelegator(delegatorVetoed).networkLimit(tanssi.subnetwork(0)),
-            deployTanssiEcosystem.MAX_NETWORK_LIMIT()
-        );
-        vm.stopPrank();
-    }
-
     function testDefaultCollateralBeingDeployedIfHolesky() public {
         vm.createSelectFork(HOLESKY_RPC);
 
@@ -569,18 +529,6 @@ contract DeployTest is Test {
         vm.stopPrank();
     }
 
-    function testDefaultCollateralNotBeingDeployedIfSepolia() public {
-        vm.chainId(11_155_111);
-        vm.startPrank(tanssi);
-        deployTanssiEcosystem.deployTanssiEcosystem(helperConfig);
-
-        (,, address defaultCollateralAddress) = deployTanssiEcosystem.ecosystemEntities();
-
-        // Verify default collateral was deployed
-        assertTrue(defaultCollateralAddress == address(0));
-        vm.stopPrank();
-    }
-
     function testRegisterEntitiesToMiddleware() public {
         vm.startPrank(tanssi);
         // Deploy full ecosystem
@@ -612,6 +560,8 @@ contract DeployTest is Test {
     }
 
     function testDeployVaultWithVaultConfiguratorEmpty() public {
+        address stEth = makeAddr("stETH");
+
         DeployVault.CreateVaultBaseParams memory params = DeployVault.CreateVaultBaseParams({
             epochDuration: VAULT_EPOCH_DURATION,
             depositWhitelist: false,
@@ -619,7 +569,7 @@ contract DeployTest is Test {
             delegatorIndex: DeployVault.DelegatorIndex.NETWORK_RESTAKE,
             shouldBroadcast: false,
             vaultConfigurator: address(0),
-            collateral: address(1),
+            collateral: stEth,
             owner: tanssi
         });
 
@@ -632,6 +582,7 @@ contract DeployTest is Test {
     //**************************************************************************************************
     function testDeployVaultWithCollateralEmpty() public {
         (, IVaultConfigurator vaultConfigurator,) = deployTanssiEcosystem.ecosystemEntities();
+        (Token stETHToken,,) = deployTanssiEcosystem.tokensAddresses();
 
         DeployVault.CreateVaultBaseParams memory params = DeployVault.CreateVaultBaseParams({
             epochDuration: VAULT_EPOCH_DURATION,
@@ -698,11 +649,12 @@ contract DeployTest is Test {
         vm.recordLogs();
 
         (, IVaultConfigurator vaultConfigurator,) = deployTanssiEcosystem.ecosystemEntities();
+        (Token stETHToken,,) = deployTanssiEcosystem.tokensAddresses();
 
         DeployVault.VaultDeployParams memory deployParams = DeployVault.VaultDeployParams({
             vaultConfigurator: address(vaultConfigurator),
             owner: tanssi,
-            collateral: address(1),
+            collateral: address(stETHToken),
             epochDuration: VAULT_EPOCH_DURATION,
             depositWhitelist: false,
             depositLimit: 0,
diff --git a/test/unit/Middleware.t.sol b/test/unit/Middleware.t.sol
index ad955326..03765453 100644
--- a/test/unit/Middleware.t.sol
+++ b/test/unit/Middleware.t.sol
@@ -39,10 +39,18 @@ import {VaultManager} from "@symbiotic-middleware/managers/VaultManager.sol";
 import {OperatorManager} from "@symbiotic-middleware/managers/OperatorManager.sol";
 import {KeyManager256} from "@symbiotic-middleware/extensions/managers/keys/KeyManager256.sol";
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {MockV3Aggregator} from "@chainlink/tests/MockV3Aggregator.sol";
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
 import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
 
 //**************************************************************************************************
 //                                      SNOWBRIDGE
@@ -62,6 +70,7 @@ import {IMiddleware} from "src/interfaces/middleware/IMiddleware.sol";
 import {IODefaultStakerRewardsFactory} from "src/interfaces/rewarder/IODefaultStakerRewardsFactory.sol";
 import {QuickSort} from "src/contracts/libraries/QuickSort.sol";
 import {DeployRewards} from "script/DeployRewards.s.sol";
+import {DeployCollateral} from "script/DeployCollateral.s.sol";
 
 import {DelegatorMock} from "../mocks/symbiotic/DelegatorMock.sol";
 import {OptInServiceMock} from "../mocks/symbiotic/OptInServiceMock.sol";
@@ -82,6 +91,8 @@ contract MiddlewareTest is Test {
     bytes32 public constant OPERATOR_KEY = bytes32(uint256(1));
     bytes32 public constant PREV_OPERATOR_KEY = bytes32(uint256(4));
     uint256 public constant PARTS_PER_BILLION = 1_000_000_000;
+    uint8 public constant ORACLE_DECIMALS = 18;
+    int256 public constant ORACLE_CONVERSION_TOKEN = 3000 ether;
 
     uint48 public constant START_TIME = 1;
 
@@ -107,8 +118,11 @@ contract MiddlewareTest is Test {
     Slasher slasher;
     VetoSlasher vetoSlasher;
     Slasher slasherWithBadType;
+    Token collateral;
+    MockV3Aggregator collateralOracle;
 
     DeployRewards deployRewards;
+    DeployCollateral deployCollateral;
     ODefaultOperatorRewards operatorRewards;
     ODefaultStakerRewardsFactory stakerRewardsFactory;
 
@@ -122,6 +136,9 @@ contract MiddlewareTest is Test {
     function setUp() public {
         vm.startPrank(owner);
 
+        deployRewards = new DeployRewards(true);
+        deployCollateral = new DeployCollateral();
+
         registry = new RegistryMock();
         operatorNetworkOptInServiceMock =
             new OptInServiceMock(address(registry), address(registry), "OperatorNetworkOptInService");
@@ -144,7 +161,11 @@ contract MiddlewareTest is Test {
         vetoSlasher =
             new VetoSlasher(vaultFactory, address(networkMiddlewareService), address(registry), slasherFactory, 1);
 
-        vault = new VaultMock(delegatorFactory, slasherFactory, vaultFactory);
+        collateral = Token(deployCollateral.deployCollateral("Token"));
+
+        collateralOracle = new MockV3Aggregator(ORACLE_DECIMALS, ORACLE_CONVERSION_TOKEN);
+
+        vault = new VaultMock(delegatorFactory, slasherFactory, vaultFactory, address(collateral));
         vault.setDelegator(address(delegator));
 
         vm.store(address(delegator), bytes32(uint256(0)), bytes32(uint256(uint160(address(vault)))));
@@ -179,6 +200,7 @@ contract MiddlewareTest is Test {
             readHelper
         );
         middleware.setGateway(address(gateway));
+        middleware.setCollateralToOracle(address(collateral), address(collateralOracle));
 
         vm.startPrank(tanssi);
         registry.register();
@@ -578,7 +600,7 @@ contract MiddlewareTest is Test {
     function testRegisterVaultWithNoHookOverwritten() public {
         // This was added just to cover the default implementation of the _beforeRegisterSharedVault hook
         SharedVaultMock vaultMock = new SharedVaultMock();
-        vaultMock.callBeforeRegisterHook(address(vaultMock), stakerRewardsParams);
+        vaultMock.callAfterRegisterHook(address(vaultMock), stakerRewardsParams);
         assertEq(vaultMock.stakeToPower(makeAddr("mock"), 1), 0);
     }
 
@@ -701,7 +723,8 @@ contract MiddlewareTest is Test {
         uint48 epochStartTs = EpochCapture(address(middleware)).getEpochStart(currentEpoch);
         uint256 stake = BaseMiddlewareReader(address(middleware)).getOperatorPowerAt(epochStartTs, operator);
 
-        assertEq(stake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(stake, expectedStake);
         vm.stopPrank();
     }
 
@@ -723,11 +746,13 @@ contract MiddlewareTest is Test {
         uint48 epochStartTs = EpochCapture(address(middleware)).getEpochStart(currentEpoch);
         uint256 stake = BaseMiddlewareReader(address(middleware)).getOperatorPowerAt(epochStartTs, operator);
 
-        assertEq(stake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(stake, expectedStake);
 
         vm.warp(NETWORK_EPOCH_DURATION * 2 + 2);
         stake = BaseMiddlewareReader(address(middleware)).getOperatorPowerAt(epochStartTs, operator);
-        assertEq(stake, OPERATOR_STAKE);
+
+        assertEq(stake, expectedStake);
         vm.stopPrank();
     }
 
@@ -769,7 +794,8 @@ contract MiddlewareTest is Test {
 
         uint256 stake = BaseMiddlewareReader(address(middleware)).getOperatorPowerAt(epochStartTs, operator);
 
-        assertEq(stake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(stake, expectedStake);
         vm.stopPrank();
     }
 
@@ -814,7 +840,8 @@ contract MiddlewareTest is Test {
         uint48 currentEpoch = middleware.getCurrentEpoch();
         uint256 totalStake = middleware.getTotalStake(currentEpoch);
 
-        assertEq(totalStake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(totalStake, expectedStake);
         vm.stopPrank();
     }
 
@@ -836,8 +863,9 @@ contract MiddlewareTest is Test {
         uint48 currentEpoch = middleware.getCurrentEpoch();
 
         uint256 totalStake = middleware.getTotalStake(currentEpoch);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
 
-        assertEq(totalStake, OPERATOR_STAKE);
+        assertEq(totalStake, expectedStake);
         vm.stopPrank();
     }
 
@@ -899,7 +927,8 @@ contract MiddlewareTest is Test {
 
         assertEq(validators.length, 1);
         assertEq(validators[0].key, OPERATOR_KEY);
-        assertEq(validators[0].stake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(validators[0].stake, expectedStake);
         vm.stopPrank();
     }
 
@@ -958,7 +987,6 @@ contract MiddlewareTest is Test {
         vm.startPrank(owner);
         vm.warp(START_TIME + SLASHING_WINDOW + 1);
         uint48 currentEpoch = middleware.getCurrentEpoch();
-        uint256 previousStake = middleware.getTotalStake(currentEpoch);
 
         // We want to slash half of it, and this is parts per billion. so this should be
         // 500000000
@@ -971,7 +999,10 @@ contract MiddlewareTest is Test {
         vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW + 1);
         currentEpoch = middleware.getCurrentEpoch();
         uint256 totalStake = middleware.getTotalStake(currentEpoch);
-        assertEq(totalStake, previousStake - slashAmount);
+        uint256 expectedStake =
+            (OPERATOR_STAKE - slashAmount) * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+
+        assertEq(totalStake, expectedStake);
         vm.stopPrank();
     }
 
@@ -1050,7 +1081,7 @@ contract MiddlewareTest is Test {
         middleware.registerSharedVault(address(vault), stakerRewardsParams);
 
         //Creating another vault to have stake on another vault
-        VaultMock vault2 = new VaultMock(delegatorFactory, slasherFactory, vaultFactory);
+        VaultMock vault2 = new VaultMock(delegatorFactory, slasherFactory, vaultFactory, address(collateral));
         DelegatorMock delegator2 = new DelegatorMock(
             address(registry),
             vaultFactory,
@@ -1091,7 +1122,9 @@ contract MiddlewareTest is Test {
         vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW + 1);
         currentEpoch = middleware.getCurrentEpoch();
         uint256 totalStake = middleware.getTotalStake(currentEpoch);
-        assertEq(totalStake, OPERATOR_STAKE / 2); //Because it slashes the operator everywhere, but the operator has stake only in vault2, since the first vault is paused
+
+        uint256 expectedStake = (OPERATOR_STAKE / 2) * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(totalStake, expectedStake); //Because it slashes the operator everywhere, but the operator has stake only in vault2, since the first vault is paused
         vm.stopPrank();
     }
 
@@ -1120,7 +1153,8 @@ contract MiddlewareTest is Test {
         vm.warp(NETWORK_EPOCH_DURATION + SLASHING_WINDOW + 1);
         currentEpoch = middleware.getCurrentEpoch();
         uint256 totalStake = middleware.getTotalStake(currentEpoch);
-        assertEq(totalStake, OPERATOR_STAKE);
+        uint256 expectedStake = OPERATOR_STAKE * uint256(ORACLE_CONVERSION_TOKEN) / 10 ** ORACLE_DECIMALS;
+        assertEq(totalStake, expectedStake);
         vm.stopPrank();
     }
 
@@ -1238,7 +1272,7 @@ contract MiddlewareTest is Test {
     // ************************************************************************************************
 
     function testDistributeRewards() public {
-        Token token = new Token("Test");
+        Token token = new Token("Test", 18);
         token.transfer(address(middleware), 1000);
 
         uint256 epoch = 0;
@@ -1276,7 +1310,7 @@ contract MiddlewareTest is Test {
         uint256 tokensInflatedToken = 1000;
         bytes32 rewardsRoot = 0x4b0ddd8b9b8ec6aec84bcd2003c973254c41d976f6f29a163054eec4e7947810;
 
-        Token token = new Token("Test");
+        Token token = new Token("Test", 18);
         token.transfer(address(middleware), 800);
 
         vm.startPrank(gateway);
@@ -1575,7 +1609,7 @@ contract MiddlewareTest is Test {
     }
 
     // ************************************************************************************************
-    // *                                  SET OPERATORs REWARD SHARE
+    // *                                  SET OPERATORS REWARD SHARE
     // ************************************************************************************************
 
     function testSetOperatorShareOnOperatorRewards() public {
@@ -1668,12 +1702,47 @@ contract MiddlewareTest is Test {
     // *                                        STAKE TO POWER
     // ************************************************************************************************
 
-    // TODO this should be improved to test the actual stake to power calculation with the value that we really want.
     function testStakeToPower() public {
-        uint256 stake = 1000;
+        uint256 stake = 1000 ether;
         address _vault = makeAddr("vault");
+        address _collateral = makeAddr("collateral");
+        address _oracle = makeAddr("oracle");
+        int256 multiplier = 5000;
+        uint8 oracleDecimals = 2;
+        uint8 tokenDecimals = 18;
+
+        vm.startPrank(owner);
+        middleware.setCollateralToOracle(_collateral, _oracle);
+        vm.stopPrank();
+        _setVaultToCollateral(_vault, _collateral);
+
+        vm.mockCall(
+            _oracle,
+            abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector),
+            abi.encode(uint80(0), multiplier, uint256(0), uint256(0), uint80(0))
+        );
+        vm.mockCall(
+            _oracle, abi.encodeWithSelector(AggregatorV3Interface.decimals.selector), abi.encode(uint8(oracleDecimals))
+        );
+        vm.mockCall(
+            _collateral, abi.encodeWithSelector(IERC20Metadata.decimals.selector), abi.encode(uint8(tokenDecimals))
+        );
+
         uint256 power = middleware.stakeToPower(_vault, stake);
-        assertEq(power, 1000);
+        uint256 expectedPower = (stake * uint256(multiplier)) / (10 ** uint256(oracleDecimals));
+        assertEq(power, expectedPower);
+    }
+
+    function testStakeToPowerWithNoOracle() public {
+        uint256 stake = 1000;
+        address _vault = makeAddr("vault");
+        address _collateral = makeAddr("collateral");
+
+        _setVaultToCollateral(_vault, _collateral);
+        // Collateral is not set to an oracle
+
+        vm.expectRevert(abi.encodeWithSelector(IMiddleware.Middleware__NotSupportedCollateral.selector, _collateral));
+        middleware.stakeToPower(_vault, stake);
     }
 
     // ************************************************************************************************
@@ -1832,4 +1901,95 @@ contract MiddlewareTest is Test {
 
         assertEq(abi.decode(key, (bytes32)), bytes32(0));
     }
+
+    // ************************************************************************************************
+    // *                                   VAULT TO COLLATERAL AND TO ORACLE
+    // ************************************************************************************************
+
+    function testVaultToCollateral() public {
+        vm.mockCall(
+            address(registry), abi.encodeWithSelector(IRegistry.isEntity.selector, address(vault)), abi.encode(true)
+        );
+        vm.startPrank(owner);
+        middleware.registerSharedVault(address(vault), stakerRewardsParams);
+        vm.stopPrank();
+
+        address currentCollateral = middleware.vaultToCollateral(address(vault));
+        assertEq(currentCollateral, address(collateral));
+    }
+
+    function testVaultToCollateralWithNoCollateral() public {
+        vm.startPrank(owner);
+        vault = new VaultMock(delegatorFactory, slasherFactory, vaultFactory, address(0));
+        vm.mockCall(
+            address(registry), abi.encodeWithSelector(IRegistry.isEntity.selector, address(vault)), abi.encode(true)
+        );
+
+        vm.expectRevert(abi.encodeWithSelector(IMiddleware.Middleware__InvalidAddress.selector));
+        middleware.registerSharedVault(address(vault), stakerRewardsParams);
+        vm.stopPrank();
+    }
+
+    function testVaultToOracle() public {
+        vm.mockCall(
+            address(registry), abi.encodeWithSelector(IRegistry.isEntity.selector, address(vault)), abi.encode(true)
+        );
+        vm.startPrank(owner);
+        middleware.registerSharedVault(address(vault), stakerRewardsParams);
+        vm.stopPrank();
+
+        address currentOracle = middleware.vaultToOracle(address(vault));
+        assertEq(currentOracle, address(collateralOracle));
+    }
+
+    // ************************************************************************************************
+    // *                                   SET COLLATERAL TO ORACLE
+    // ************************************************************************************************
+
+    function testSetCollateralToOracle() public {
+        address _collateral = makeAddr("collateral");
+        address _oracle = makeAddr("oracle");
+
+        vm.startPrank(owner);
+        middleware.setCollateralToOracle(_collateral, _oracle);
+        vm.stopPrank();
+        assertEq(middleware.collateralToOracle(_collateral), _oracle);
+    }
+
+    function testSetCollateralToOracleNoCollateral() public {
+        address _collateral = address(0);
+        address _oracle = makeAddr("oracle");
+
+        vm.startPrank(owner);
+        vm.expectRevert(abi.encodeWithSelector(IMiddleware.Middleware__InvalidAddress.selector));
+        middleware.setCollateralToOracle(_collateral, _oracle);
+        vm.stopPrank();
+    }
+
+    function testSetCollateralToOracleRemoveOracle() public {
+        address _collateral = makeAddr("collateral");
+        address _oracle = makeAddr("oracle");
+
+        vm.startPrank(owner);
+        middleware.setCollateralToOracle(_collateral, _oracle);
+        middleware.setCollateralToOracle(_collateral, address(0));
+        vm.stopPrank();
+        assertEq(middleware.collateralToOracle(_collateral), address(0));
+    }
+
+    // ************************************************************************************************
+    // *                                          INTERNAL
+    // ************************************************************************************************
+
+    function _setVaultToCollateral(address vault_, address collateral_) internal {
+        // Taken from MiddlewareStorage.sol
+        bytes32 MIDDLEWARE_STORAGE_LOCATION = 0xca64b196a0d05040904d062f739ed1d1e1d3cc5de78f7001fb9039595fce9100;
+
+        bytes32 slot = bytes32(uint256(MIDDLEWARE_STORAGE_LOCATION) + uint256(2)); // 2 is mapping slot number for the vault to collateral
+        // Get slot for mapping with vault_
+        slot = keccak256(abi.encode(vault_, slot));
+
+        // Store array length
+        vm.store(address(middleware), slot, bytes32(uint256(uint160(collateral_))));
+    }
 }
diff --git a/test/unit/Rewards.t.sol b/test/unit/Rewards.t.sol
index aa5662aa..77aa4157 100644
--- a/test/unit/Rewards.t.sol
+++ b/test/unit/Rewards.t.sol
@@ -29,6 +29,11 @@ import {IRegistry} from "@symbiotic/interfaces/common/IRegistry.sol";
 import {IVaultStorage} from "@symbiotic/interfaces/vault/IVaultStorage.sol";
 import {BaseMiddlewareReader} from "@symbiotic-middleware/middleware/BaseMiddlewareReader.sol";
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {MockV3Aggregator} from "@chainlink/tests/MockV3Aggregator.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
@@ -63,6 +68,7 @@ import {Token} from "../mocks/Token.sol";
 import {MockFeeToken} from "../mocks/FeeToken.sol";
 
 import {DeployRewards} from "script/DeployRewards.s.sol";
+import {DeployCollateral} from "script/DeployCollateral.s.sol";
 
 contract RewardsTest is Test {
     uint48 public constant NETWORK_EPOCH_DURATION = 6 days;
@@ -73,6 +79,8 @@ contract RewardsTest is Test {
     uint256 public constant EXPECTED_CLAIMABLE = uint256(AMOUNT_TO_CLAIM) * TOKENS_PER_POINT;
     uint256 public constant ADMIN_FEE = 800; // 8%
     uint48 public constant OPERATOR_SHARE = 2000;
+    uint8 public constant ORACLE_DECIMALS = 18;
+    int256 public constant ORACLE_CONVERSION_TOKEN = 3000;
 
     // Root hash of the rewards merkle tree. It represents the rewards for the epoch 0 for alice and bob with 20 points each
     bytes32 public constant REWARDS_ROOT = 0x4b0ddd8b9b8ec6aec84bcd2003c973254c41d976f6f29a163054eec4e7947810;
@@ -105,6 +113,7 @@ contract RewardsTest is Test {
     Token token;
     MockFeeToken feeToken;
     DeployRewards deployRewards;
+    DeployCollateral deployCollateral;
 
     function setUp() public {
         //Extract rewards data from json
@@ -130,6 +139,7 @@ contract RewardsTest is Test {
         address readHelper = address(new BaseMiddlewareReader());
 
         deployRewards = new DeployRewards(true);
+        deployCollateral = new DeployCollateral();
         address operatorRewardsAddress = deployRewards.deployOperatorRewardsContract(
             tanssi, address(networkMiddlewareService), OPERATOR_SHARE, owner
         );
@@ -144,7 +154,10 @@ contract RewardsTest is Test {
             0
         );
 
-        vault = new VaultMock(delegatorFactory, slasherFactory, address(vaultFactory));
+        token = new Token("Token", 18);
+        MockV3Aggregator collateralOracle = new MockV3Aggregator(ORACLE_DECIMALS, ORACLE_CONVERSION_TOKEN);
+
+        vault = new VaultMock(delegatorFactory, slasherFactory, address(vaultFactory), address(token));
         vault.setDelegator(address(delegator));
         vm.store(address(delegator), bytes32(uint256(0)), bytes32(uint256(uint160(address(vault)))));
 
@@ -179,9 +192,10 @@ contract RewardsTest is Test {
         );
         slasher = new Slasher(address(vaultFactory), address(networkMiddlewareService), slasherFactory, 0);
 
+        token.transfer(address(middleware), token.totalSupply());
+
         vm.startPrank(tanssi);
-        token = new Token("Test");
-        token.transfer(address(middleware), token.balanceOf(tanssi));
+        middleware.setCollateralToOracle(address(token), address(collateralOracle));
         networkRegistry.registerNetwork();
         networkMiddlewareService.setMiddleware(address(middleware));
 
@@ -853,7 +867,7 @@ contract RewardsTest is Test {
     function testClaimableButWithFakeTokenAddressButMultipleRewards() public {
         uint48 epoch = 0;
         uint48 epochTs = middleware.getEpochStart(epoch);
-        Token newToken = new Token("NewToken");
+        Token newToken = new Token("NewToken", 18);
 
         _setRewardsMapping(epoch, true, address(newToken));
 
@@ -1085,7 +1099,7 @@ contract RewardsTest is Test {
     function testClaimStakerRewardsButMultipleRewards() public {
         uint48 epoch = 0;
         uint48 epochTs = middleware.getEpochStart(epoch);
-        Token newToken = new Token("NewToken");
+        Token newToken = new Token("NewToken", 18);
         newToken.transfer(address(middleware), AMOUNT_TO_DISTRIBUTE);
 
         _setRewardsMapping(epoch, true, address(newToken));


Reviews:
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T12:41:12Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T17:05:59Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T17:06:27Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T17:17:34Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T17:22:14Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:05:56Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:07:51Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:09:51Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:18:24Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:22:02Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:23:18Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:37:30Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:38:33Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:39:30Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:41:15Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T11:42:23Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T12:32:05Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T12:35:19Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T13:38:09Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T13:38:27Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T14:27:10Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:25:03Z
user: login: steven2308
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:26:43Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:31:24Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:32:09Z
user: login: NindoK
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:32:41Z
user: login: NindoK
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:49:50Z

Review comments:
diff_hunk: @@ -22,6 +22,7 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
     ) public checkAccess {
         _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
         _registerSharedVault(sharedVault);
+        _afterRegisterSharedVault(sharedVault);
path: src/contracts/extensions/OSharedVaults.sol
user: login: NindoK
body: What if we do it in the beforeRegisterSharedVault?
created_at: 2025-03-21T08:45:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,6 +44,7 @@ import {IOGateway} from "@tanssi-bridge-relayer/snowbridge/contracts/src/interfa
 import {IODefaultStakerRewards} from "src/interfaces/rewarder/IODefaultStakerRewards.sol";
 import {IODefaultOperatorRewards} from "src/interfaces/rewarder/IODefaultOperatorRewards.sol";
 import {IODefaultStakerRewardsFactory} from "src/interfaces/rewarder/IODefaultStakerRewardsFactory.sol";
+import {IAggregatorV3} from "src/interfaces/IAggregatorV3.sol";
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Placeholder to remember to change to chainlink one
created_at: 2025-03-21T08:45:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -247,15 +286,15 @@ contract Middleware is
     //TODO use prebuilt function from SDK
     function slash(
         uint48 epoch,
-        bytes32 operatorKey,
+        bytes32 operatorKey_,
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Out of curiosity why? xD 
created_at: 2025-03-21T08:48:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,10 +26,13 @@ abstract contract MiddlewareStorage {
         mapping(uint48 epoch => uint256 amount) totalStakeCache;
         mapping(uint48 epoch => bool) totalStakeIsCached;
         mapping(uint48 epoch => mapping(address operator => uint256 amount)) operatorStakeCache;
+        uint256 minStake;
+        mapping(address collateral => address oracle) collateralToOracle;
+        mapping(address vault => address collateral) vaultToCollateral;
     }
 
     // keccak256(abi.encode(uint256(keccak256("tanssi.middleware.MiddlewareStorage.v1")) - 1)) & ~bytes32(uint256(0xff));
-    bytes32 private constant MIDDLEWARE_STORAGE_LOCATION =
+    bytes32 public constant MIDDLEWARE_STORAGE_LOCATION =
path: src/contracts/middleware/MiddlewareStorage.sol
user: login: NindoK
body: Keep them private. To test them just copy them in the tests
created_at: 2025-03-21T08:49:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -613,14 +613,16 @@ contract DeployTest is Test {
     }
 
     function testDeployVaultWithVaultConfiguratorEmpty() public {
+        (Token stETHToken,,) = deployTanssiEcosystem.tokensAddresses();
+
         DeployVault.CreateVaultBaseParams memory params = DeployVault.CreateVaultBaseParams({
             epochDuration: VAULT_EPOCH_DURATION,
             depositWhitelist: false,
             depositLimit: 0,
             delegatorIndex: DeployVault.DelegatorIndex.NETWORK_RESTAKE,
             shouldBroadcast: false,
             vaultConfigurator: address(0),
-            collateral: address(1),
path: test/unit/Deploy.t.sol
user: login: NindoK
body: We don't really need the collateral right? You can just create an address "stETH" and use that one if we want to be more specific
created_at: 2025-03-21T08:52:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -52,4 +53,11 @@ contract DeployCollateral is Script {
 
         return tokenAddress;
     }
+
+    function deployMockOracle(uint8 decimals, int256 answer) public returns (address) {
path: script/DeployCollateral.s.sol
user: login: NindoK
body: I would keep this script to only deploy collateral. The oracle can be deployed manually
created_at: 2025-03-21T08:54:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,7 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
     ) public checkAccess {
         _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
         _registerSharedVault(sharedVault);
+        _afterRegisterSharedVault(sharedVault);
path: src/contracts/extensions/OSharedVaults.sol
user: login: steven2308
body: I'm using this hook to map vault to collateral. To do that I need to call `IVault(vault).collateral()` or similar.
If I do that on the the before hook, it might be that the vault is not a valid vault so it doesn't have the collateral method, which produces an unexpected revert.
If I do it after, there's no such issue, since the `_register` method checks for its validity.  So there were 3 options:
1. Assume the vault is good. (I don't like assuming)
2. Check the vault is valid on the `_before` hook or when getting the collateral. (Would do double check).
3. Create and after hook. I went for this.

Now that I think of it I could also have a sort of try catch when getting the collateral, but feels like a weird practice in Solidity IMO. 


created_at: 2025-03-21T17:05:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -247,15 +286,15 @@ contract Middleware is
     //TODO use prebuilt function from SDK
     function slash(
         uint48 epoch,
-        bytes32 operatorKey,
+        bytes32 operatorKey_,
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: It was shadowing the state variable or the getter.
created_at: 2025-03-21T17:06:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,7 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
     ) public checkAccess {
         _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
         _registerSharedVault(sharedVault);
+        _afterRegisterSharedVault(sharedVault);
path: src/contracts/extensions/OSharedVaults.sol
user: login: NindoK
body: Actually no it totally makes sense and I like it better, good one. But at this point I would also move what we have in the beforeRegister to the after. Since "theoretically" we want to deploy the contract once it has been validated
created_at: 2025-03-21T17:17:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,6 +22,7 @@ abstract contract OSharedVaults is BaseMiddleware, IOSharedVaults {
     ) public checkAccess {
         _beforeRegisterSharedVault(sharedVault, stakerRewardsParams);
         _registerSharedVault(sharedVault);
+        _afterRegisterSharedVault(sharedVault);
path: src/contracts/extensions/OSharedVaults.sol
user: login: steven2308
body: Moved to after hook.
created_at: 2025-03-25T17:22:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,3 +32,6 @@
 [submodule "lib/middleware-sdk"]
 	path = lib/middleware-sdk
 	url = https://github.com/symbioticfi/middleware-sdk
+[submodule "lib/chainlink-local"]
path: .gitmodules
user: login: NindoK
body: Use the one that I have on my branch
created_at: 2025-03-26T11:05:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,18 +71,24 @@ contract Middleware is
     using Subnetwork for address;
     using Math for uint256;
 
+    modifier notZeroAddress(
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Not a fan of this. I don't think it optimize the code and it increases code length
created_at: 2025-03-26T11:07:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,6 +177,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Do we need here the check on zero address? Probably not right?
created_at: 2025-03-26T11:09:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -494,175 +507,115 @@ contract MiddlewareTest is Test {
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
+        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
 
+        (uint256 totalOperator2StakeAfter,) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndVetoingSlash() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 = (SLASH_AMOUNT * remainingOperator3Stake) / totalOperator3Stake;
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * (powerFromSharesOperator3 / 2)) / PARTS_PER_BILLION;
path: test/fork/Middleware.t.sol
user: login: NindoK
body: Why 10% has been used instead of the slashingFraction coming from the slashedAmount?
created_at: 2025-03-26T11:18:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,6 +114,17 @@ contract MiddlewareTest is Test {
     uint256 public constant OPERATOR_SHARE = 1;
     uint256 public constant TOTAL_NETWORK_SHARES = 3;
     uint256 public constant PARTS_PER_BILLION = 1_000_000_000;
+    uint256 public constant SLASHING_FRACTION = PARTS_PER_BILLION / 10; // 10%
+
+    // TODO Steven: Change to realistic values
+    uint8 public constant ORACLE_DECIMALS = 10; // TODO Steven: This decimals are actually on staked value, not on price
path: test/integration/Middleware.t.sol
user: login: NindoK
body: Why not standard 18?
created_at: 2025-03-26T11:22:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -362,11 +388,24 @@ contract MiddlewareTest is Test {
         vm.stopPrank();
     }
 
-    function _registerOperator(address _operator, address _network, address _vault) public {
+    function _registerOperatorAndOptIn(
+        address _operator,
+        address _network,
+        address _vault,
+        address vault2,
+        address vault3
path: test/integration/Middleware.t.sol
user: login: NindoK
body: Wouldn't it be better to have a boolean is that conditionally registerOperator and optIn to the network, so we can reuse the same function for whatever vault. Otherwise if we will need 4 vaults we will have to extend this
created_at: 2025-03-26T11:23:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,18 +71,24 @@ contract Middleware is
     using Subnetwork for address;
     using Math for uint256;
 
+    modifier notZeroAddress(
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: This actually reduced the contract enough to fit.  The trick is using an internal function in the modifier.
created_at: 2025-03-26T11:37:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,6 +177,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: It wouldn't make sense to set zero address to an oracle. I think the check is important for collateral.
For oracle, I'm not checking so you can set the oracle of a collateral to zero, in case you don't want to support it anymore.
created_at: 2025-03-26T11:38:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -494,175 +507,115 @@ contract MiddlewareTest is Test {
         uint48 newEpoch = ecosystemEntities.middleware.getCurrentEpoch();
         validators = ecosystemEntities.middleware.getValidatorSet(newEpoch);
 
-        activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-        (uint256 totalOperator2StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, slashAmountSlashable);
+        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
+        uint256 activePowerInVetoed = (activeStakeInVetoed * uint256(ORACLE_CONVERSION_TOKEN)) / 10 ** ORACLE_DECIMALS;
 
+        (uint256 totalOperator2StakeAfter,) =
+            _calculateOperatorPower(totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
         (uint256 totalOperator3StakeAfter,) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2 * 2, activeStakeInVetoed, slashAmountSlashable);
+            _calculateOperatorPower(totalPowerVault + totalPowerVaultSlashable, activePowerInVetoed, slashingPower);
+
         assertEq(validators[1].stake, totalOperator2StakeAfter);
         assertEq(validators[2].stake, totalOperator3StakeAfter);
     }
 
     function testSlashingOnOperator3AndVetoingSlash() public {
-        vm.warp(block.timestamp + NETWORK_EPOCH_DURATION + SLASHING_WINDOW - 1);
-        uint48 currentEpoch = ecosystemEntities.middleware.getCurrentEpoch();
-
-        Middleware.ValidatorData[] memory validators = ecosystemEntities.middleware.getValidatorSet(currentEpoch);
-        //Since vaultVetoed is full restake, it exactly gets the amount deposited, so no need to calculations
-        uint256 activeStakeInVetoed = ecosystemEntities.vaultVetoed.activeStake();
-
-        (uint256 totalOperator2Stake,) = _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        (uint256 totalOperator3Stake, uint256 remainingOperator3Stake) =
-            _calculateTotalOperatorStake(OPERATOR_STAKE * 2, activeStakeInVetoed, 0);
-
-        assertEq(validators[1].stake, totalOperator2Stake);
-        //We need to assert like this instead of putting OPERATOR_STAKE * 2 * 2 because of the precision loss. We know that remainingOperator3Stake will be the same even for the other (non slashable) vault so we can just sum it.
-        assertEq(validators[2].stake, totalOperator3Stake + remainingOperator3Stake);
+        (uint48 currentEpoch, Middleware.ValidatorData[] memory validators,,,, uint256 powerFromSharesOperator3) =
+            _prepareSlashingTest();
 
-        //We calculate the amount slashable for only the operator3 since it's the only one that should be slashed. As a side effect operator2 will be slashed too since it's taking part in a NetworkRestake delegator based vault
-        uint256 slashAmountSlashable3 = (SLASH_AMOUNT * remainingOperator3Stake) / totalOperator3Stake;
-        uint256 slashedAmount = 30 ether;
-        // We want to slash 30 ether, so we need to calculate what percentage
-        uint256 slashingFraction = slashedAmount.mulDiv(PARTS_PER_BILLION, totalOperator3Stake);
+        // We only take half of the operator3 shares, since only its participation on vaultSlashable will be slashed, regular vault isn't affected
+        uint256 slashingPower = (SLASHING_FRACTION * (powerFromSharesOperator3 / 2)) / PARTS_PER_BILLION;
path: test/fork/Middleware.t.sol
user: login: steven2308
body: Previously tests were using an amount to calculate the fraction (10% here).  I didn't see the point to start from amount if you need % anyway.
created_at: 2025-03-26T11:39:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,6 +114,17 @@ contract MiddlewareTest is Test {
     uint256 public constant OPERATOR_SHARE = 1;
     uint256 public constant TOTAL_NETWORK_SHARES = 3;
     uint256 public constant PARTS_PER_BILLION = 1_000_000_000;
+    uint256 public constant SLASHING_FRACTION = PARTS_PER_BILLION / 10; // 10%
+
+    // TODO Steven: Change to realistic values
+    uint8 public constant ORACLE_DECIMALS = 10; // TODO Steven: This decimals are actually on staked value, not on price
path: test/integration/Middleware.t.sol
user: login: steven2308
body: Actually this makes more sense to be way lower but I forgot to change. This doesn't tell you the decimals of the asset, it tells you the decimals of the conversion. So for instance, for an asset at 1234.56 the oracle would answer 123456 with 2 decimals.

This makes me remember, we should actually be checking decimals from each asset when calculating the power, right?
created_at: 2025-03-26T11:41:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -362,11 +388,24 @@ contract MiddlewareTest is Test {
         vm.stopPrank();
     }
 
-    function _registerOperator(address _operator, address _network, address _vault) public {
+    function _registerOperatorAndOptIn(
+        address _operator,
+        address _network,
+        address _vault,
+        address vault2,
+        address vault3
path: test/integration/Middleware.t.sol
user: login: steven2308
body: I like it, will do.
created_at: 2025-03-26T11:42:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,6 +177,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Yes it doesn't make sense but I don't think it's an issue per se. By taking this out we are taking out a bit of code too!
created_at: 2025-03-26T12:32:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,18 +71,24 @@ contract Middleware is
     using Subnetwork for address;
     using Math for uint256;
 
+    modifier notZeroAddress(
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: Oh cool. Wouldn't just the if be the same if not shorter? Or it's because with an internal function there is a pointer and thus you don't copy paste the code every time the modifier is used?
created_at: 2025-03-26T12:35:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,6 +177,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: Was this meant for another comment? :sweat_smile: 
created_at: 2025-03-26T13:38:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -65,18 +71,24 @@ contract Middleware is
     using Subnetwork for address;
     using Math for uint256;
 
+    modifier notZeroAddress(
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: Exactly, that's the trick.
created_at: 2025-03-26T13:38:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.25;
+
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+/**
+ * @title IOERC20
+ * @notice Interface for managing ERC20 tokens, including decimals
+ */
+
+interface IOERC20 is IERC20 {
path: src/interfaces/extensions/IOERC20.sol
user: login: NindoK
body: Use IERC20Metadata from OZ
created_at: 2025-03-27T14:27:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,19 +14,26 @@
 // along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
 pragma solidity 0.8.25;
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: You can take this out now right?
created_at: 2025-03-27T15:25:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,19 +14,26 @@
 // along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
 pragma solidity 0.8.25;
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
path: src/contracts/middleware/Middleware.sol
user: login: steven2308
body: Nope, it's used on `distributeRewards` to get balance and approve.
created_at: 2025-03-27T15:26:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,19 +14,26 @@
 // along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
 pragma solidity 0.8.25;
 
+//**************************************************************************************************
+//                                      CHAINLINK
+//**************************************************************************************************
+import {AggregatorV3Interface} from "@chainlink/shared/interfaces/AggregatorV2V3Interface.sol";
+
 //**************************************************************************************************
 //                                      OPENZEPPELIN
 //**************************************************************************************************
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: You are right. Dunno why it didn't popped up on my browser :/ 
created_at: 2025-03-27T15:31:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -153,6 +177,18 @@ contract Middleware is
         _grantRole(GATEWAY_ROLE, _gateway);
     }
 
+    function setCollateralToOracle(
+        address collateral,
+        address oracle
+    ) external checkAccess notZeroAddress(collateral) {
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: No it was actually correct. But given that we will change the layout it's fine
created_at: 2025-03-27T15:32:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -126,19 +141,34 @@ contract Middleware is
     /**
      * @inheritdoc OSharedVaults
      */
-    function _beforeRegisterSharedVault(
+    function _afterRegisterSharedVault(
         address sharedVault,
         IODefaultStakerRewards.InitParams memory stakerRewardsParams
     ) internal virtual override {
         address stakerRewards =
             IODefaultStakerRewardsFactory(i_stakerRewardsFactory).create(sharedVault, stakerRewardsParams);
 
         IODefaultOperatorRewards(i_operatorRewards).setStakerRewardContract(stakerRewards, sharedVault);
+
+        address collateral = IVault(sharedVault).collateral();
+        _setVaultToCollateral(sharedVault, collateral);
     }
 
-    // TODO: this should probably take into account underlying asset price and return a power which could be either the total value of the stake in usd or a value that makes sense for us
     function stakeToPower(address vault, uint256 stake) public view override returns (uint256 power) {
-        return stake;
+        address collateral = vaultToCollateral(vault);
+        address oracle = collateralToOracle(collateral);
+
+        if (oracle == address(0)) {
+            revert Middleware__NotSupportedCollateral(collateral);
+        }
+        (, int256 price,,,) = AggregatorV3Interface(oracle).latestRoundData();
+        uint8 priceDecimals = AggregatorV3Interface(oracle).decimals();
+        power = stake.mulDiv(uint256(price), 10 ** priceDecimals);
+        // Normalize power to 18 decimals
path: src/contracts/middleware/Middleware.sol
user: login: NindoK
body: I'm still thinking that this is wrong. We need a better formula, but we can do it in another PR
created_at: 2025-03-27T15:32:41Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-26T17:08:26Z
body: <h2>> Coverage generated</h2>Coverage after merging <b>feat/chainlink-price-feed</b> into <b>staging</b> will be<table><tbody><tr><th>100.00%</th><th> 0.00%</th></tr></tbody></table>

<details><summary>Coverage Report</summary><table><tbody><tr><th>File</th><th>Stmts</th><th>Branches</th><th>Funcs</th><th>Lines</th><th>Uncovered Lines</th></tr><tr><td colspan='6'><b>script</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/DeployCollateral.s.sol'>DeployCollateral.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/DeployRewards.s.sol'>DeployRewards.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/DeploySymbiotic.s.sol'>DeploySymbiotic.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/DeployTanssiEcosystem.s.sol'>DeployTanssiEcosystem.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/DeployVault.s.sol'>DeployVault.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/script/HelperConfig.s.sol'>HelperConfig.s.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>src/contracts/extensions</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/extensions/OSharedVaults.sol'>OSharedVaults.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>src/contracts/libraries</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/libraries/QuickSort.sol'>QuickSort.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>src/contracts/middleware</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/middleware/Middleware.sol'>Middleware.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/middleware/MiddlewareStorage.sol'>MiddlewareStorage.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>src/contracts/rewarder</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/rewarder/ODefaultOperatorRewards.sol'>ODefaultOperatorRewards.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/rewarder/ODefaultStakerRewards.sol'>ODefaultStakerRewards.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/src/contracts/rewarder/ODefaultStakerRewardsFactory.sol'>ODefaultStakerRewardsFactory.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>test/mocks</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/mocks/FeeToken.sol'>FeeToken.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/mocks/Token.sol'>Token.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>test/mocks/symbiotic</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/mocks/symbiotic/DelegatorMock.sol'>DelegatorMock.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/mocks/symbiotic/RegistryMock.sol'>RegistryMock.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/mocks/symbiotic/SharedVaultMock.sol'>SharedVaultMock.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td colspan='6'><b>test/unit/utils</b></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/unit/utils/MiddlewareV2.sol'>MiddlewareV2.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr><tr><td>&nbsp; &nbsp;<a href='https://github.com/moondance-labs/tanssi-symbiotic/blob/70e1efbad2f14ec04457e153e900e589ecb1e682/test/unit/utils/MiddlewareV3.sol'>MiddlewareV3.sol</a></td><td>100%</td><td>100%</td><td>100%</td><td>100%</td><td></td></tr></tbody></table></details>
author_association: NONE