Skip to content

Commit

Permalink
fix: _checkHasRole helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeday committed Dec 25, 2024
1 parent e794d76 commit e9b6c01
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 20 deletions.
47 changes: 27 additions & 20 deletions contracts/0.8.25/vaults/VaultHubViewerV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ interface IVaultHub {
}

contract VaultHubViewerV1 {
bytes32 constant strictTrue = keccak256(hex"0000000000000000000000000000000000000000000000000000000000000001");

IVaultHub public immutable vaultHub;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

Expand All @@ -43,38 +45,26 @@ contract VaultHubViewerV1 {
/// @param _owner The address to check
/// @return True if the address is the owner, false otherwise
function isOwner(IVault vault, address _owner) public view returns (bool) {
address currentOwner = vault.owner();
if (currentOwner == _owner) {
address vaultOwner = vault.owner();
if (vaultOwner == _owner) {
return true;
}
if (isContract(currentOwner)) {
try IDashboardACL(currentOwner).hasRole(DEFAULT_ADMIN_ROLE, _owner) returns (bool result) {
return result;
} catch {
return false;
}
}
return false;

return _checkHasRole(vaultOwner, _owner, DEFAULT_ADMIN_ROLE);
}

/// @notice Checks if a given address has a given role on a vault
/// @notice Checks if a given address has a given role on a vault owner contract
/// @param vault The vault to check
/// @param _member The address to check
/// @param _role The role to check
/// @return True if the address has the role, false otherwise
function hasRole(IVault vault, address _member, bytes32 _role) public view returns (bool) {
address owner = vault.owner();
if (owner == address(0)) {
address vaultOwner = vault.owner();
if (vaultOwner == address(0)) {
return false;
}

if (!isContract(owner)) return false;

try IDashboardACL(owner).hasRole(_role, _member) returns (bool result) {
return result;
} catch {
return false;
}
return _checkHasRole(vaultOwner, _member, _role);
}

/// @notice Returns all vaults owned by a given address
Expand Down Expand Up @@ -118,6 +108,23 @@ contract VaultHubViewerV1 {
return _filterNonZeroVaults(vaults, valid);
}

/// @notice safely returns if role member has given role
/// @param _contract that can have ACL or not
/// @param _member addrress to check for role
/// @return _role ACL role bytes
function _checkHasRole(address _contract, address _member, bytes32 _role) internal view returns (bool) {
if (!isContract(_contract)) return false;

bytes memory payload = abi.encodeWithSignature("hasRole(bytes32,address)", _role, _member);
(bool success, bytes memory result) = _contract.staticcall(payload);

if (success && keccak256(result) == strictTrue) {
return true;
} else {
return false;
}
}

/// @notice Filters out zero address vaults from an array
/// @param _vaults Array of vaults to filter
/// @param _validCount number of non-zero vaults
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
// for testing purposes only

pragma solidity 0.8.25;

import {VaultHub} from "contracts/0.8.25/vaults/VaultHub.sol";

//
// This mock represents custom Vault owner contract, that does not have ACL e.g. Safe
//
contract CustomOwner__MockForHubViewer {
bool public shouldRevertFallback;

constructor() {
shouldRevertFallback = true;
}

function setShouldRevertFallback(bool _shouldRevertFallback) external {
shouldRevertFallback = _shouldRevertFallback;
}

fallback() external {
if (shouldRevertFallback) revert("fallback");
}

receive() external payable {
if (shouldRevertFallback) {
revert("fallback");
}
}
}
38 changes: 38 additions & 0 deletions test/0.8.25/vaults/vault-hub-viewer/vault-hub-viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ethers } from "hardhat";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";

import {
CustomOwner__MockForHubViewer,
Dashboard,
Delegation,
DepositContract__MockForStakingVault,
Expand Down Expand Up @@ -93,6 +94,24 @@ const deployVaultDashboard = async (
return { vaultDashboard, dashboard };
};

const deployCustomOwner = async (vaultImpl: StakingVault, operator: HardhatEthersSigner) => {
const customOwner = await ethers.deployContract("CustomOwner__MockForHubViewer");
// deploying factory/beacon
const factoryStakingVault = await ethers.deployContract("VaultFactory__MockForStakingVault", [
await vaultImpl.getAddress(),
]);
const vaultCreation = await factoryStakingVault
.createVault(await customOwner.getAddress(), await operator.getAddress())
.then((tx) => tx.wait());
if (!vaultCreation) throw new Error("Vault creation failed");
const events = findEvents(vaultCreation, "VaultCreated");
if (events.length != 1) throw new Error("There should be exactly one VaultCreated event");
const vaultCreatedEvent = events[0];

const stakingVault = StakingVault__factory.connect(vaultCreatedEvent.args.vault);
return { stakingVault, customOwner };
};

const deployStakingVault = async (
vaultImpl: StakingVault,
vaultOwner: HardhatEthersSigner,
Expand Down Expand Up @@ -139,9 +158,12 @@ describe("VaultHubViewerV1", () => {
let stakingVault: StakingVault;
let vaultDashboard: StakingVault;
let vaultDelegation: StakingVault;
let vaultCustom: StakingVault;
let vaultHubViewer: VaultHubViewerV1;

let dashboard: Dashboard;
let delegation: Delegation;
let customOwnerContract: CustomOwner__MockForHubViewer;

let originalState: string;

Expand All @@ -160,6 +182,7 @@ describe("VaultHubViewerV1", () => {
dashboardImpl = await ethers.deployContract("Dashboard", [steth, weth, wsteth]);
delegationImpl = await ethers.deployContract("Delegation", [steth, weth, wsteth]);

// Delegation controlled vault
const delegationResult = await deployVaultDelegation(
vaultImpl,
delegationImpl,
Expand All @@ -171,12 +194,19 @@ describe("VaultHubViewerV1", () => {
vaultDelegation = delegationResult.vaultDelegation;
delegation = delegationResult.delegation;

// Dashboard conntroled vault
const dashboardResult = await deployVaultDashboard(vaultImpl, dashboardImpl, factoryOwner, vaultOwner, operator);
vaultDashboard = dashboardResult.vaultDashboard;
dashboard = dashboardResult.dashboard;

// EOA controlled vault
stakingVault = await deployStakingVault(vaultImpl, vaultOwner, operator);

// Custom owner controlled vault
const customdResult = await deployCustomOwner(vaultImpl, operator);
vaultCustom = customdResult.stakingVault;
customOwnerContract = customdResult.customOwner;

vaultHubViewer = await ethers.deployContract("VaultHubViewerV1", [hub]);
expect(await vaultHubViewer.vaultHub()).to.equal(hub);

Expand Down Expand Up @@ -204,6 +234,7 @@ describe("VaultHubViewerV1", () => {
await hub.connect(hubSigner).mock_connectVault(vaultDelegation.getAddress());
await hub.connect(hubSigner).mock_connectVault(vaultDashboard.getAddress());
await hub.connect(hubSigner).mock_connectVault(stakingVault.getAddress());
await hub.connect(hubSigner).mock_connectVault(vaultCustom.getAddress());
});

it("returns all vaults owned by a given address", async () => {
Expand All @@ -213,13 +244,20 @@ describe("VaultHubViewerV1", () => {
expect(vaults[1]).to.equal(vaultDashboard);
expect(vaults[2]).to.equal(stakingVault);
});

it("returns correct owner for custom vault", async () => {
const vaults = await vaultHubViewer.vaultsByOwner(customOwnerContract.getAddress());
expect(vaults.length).to.equal(1);
expect(vaults[0]).to.equal(vaultCustom);
});
});

context("vaultsByRole", () => {
beforeEach(async () => {
await hub.connect(hubSigner).mock_connectVault(vaultDelegation.getAddress());
await hub.connect(hubSigner).mock_connectVault(vaultDashboard.getAddress());
await hub.connect(hubSigner).mock_connectVault(stakingVault.getAddress());
await hub.connect(hubSigner).mock_connectVault(vaultCustom.getAddress());
});

it("returns all vaults with a given role on Delegation", async () => {
Expand Down

0 comments on commit e9b6c01

Please sign in to comment.