Demo vault

solidity-vault · solidity
Units
6
Entries
21
Edges
18
Resolution
94%
1 unresolved
Fireable alone
9
End state
0
Most wiring resolved. Read the holes before relying on the graph.

Holes — what could not be determined

These are edges whose target could not be determined from source. They are not guessed and not omitted — they are the places to read by hand.

EntryKindSource lineFile
Vault.execute call (bool ok, bytes memory out) = target.delegatecall(data); src/Vault.sol

Surface — what one principal can do alone

Entries that change state and need at most one principal’s consent. anyone means no access check at all — the most open case there is, so those sort first.

AloneEntryEffectMovesHolesWhere
anyone Vault.deposit changes it 0 0 src/Vault.sol:22
anyone Vault.withdraw changes it 0 0 src/Vault.sol:28
anyone Vault.takeFee changes it 0 0 src/Vault.sol:41
anyone IERC20.transfer changes it 0 0 src/Strategy.sol:5
anyone IERC20.transferFrom changes it 0 0 src/Strategy.sol:6
anyone IRouter.swap changes it 0 0 src/Strategy.sol:11
anyone IStrategy.invest changes it 0 0 src/Strategy.sol:15
anyone IStrategy.withdraw changes it 0 0 src/Strategy.sol:16
anyone IStrategy.harvest changes it 0 0 src/Strategy.sol:17
anyone Strategy.constructor changes it 0 0 src/Strategy.sol:26
anyone Vault.constructor changes it 0 0 src/Vault.sol:20
onlyOwner Vault.execute changes it 0 1 src/Vault.sol:46
keepers[msg.sender] Vault.harvest changes it 0 0 src/Vault.sol:34
vault Strategy.harvest changes it 0 0 src/Strategy.sol:37
vault Strategy.withdraw changes it 0 0 src/Strategy.sol:32
vault Strategy.emergencyExit changes it 0 0 src/Strategy.sol:44
vault Strategy.invest changes it 0 0 src/Strategy.sol:30
onlyOwner Owned.transferOwnership changes it 0 0 src/Vault.sol:10
onlyOwner Vault.setStrategy changes it 0 0 src/Vault.sol:43
onlyOwner Vault.setKeeper changes it 0 0 src/Vault.sol:44

Wiring

FromToVia
Strategy call IERC20 withdraw, harvest, harvest, emergencyExit
Strategy call IRouter harvest
Vault call IERC20 deposit, deposit, deposit, withdraw, harvest, harvest, harvest, takeFee
Vault call IStrategy deposit, withdraw, harvest, harvest

Unresolved edges are deliberately absent here — they are in Holes above. A graph that quietly drew a guessed target would be worse than one with a gap in it.

Units

IERC20 src/Strategy

Signatories—
Wheresrc/Strategy.sol:4

IERC20.transfer changes it declared only

Authority—
Returnsbool
Wheresrc/Strategy.sol:5
2 argument(s)
toaddress
amountuint256

IERC20.transferFrom changes it declared only

Authority—
Returnsbool
Wheresrc/Strategy.sol:6
3 argument(s)
fromaddress
toaddress
amountuint256

IERC20.balanceOf repeatable declared only

Authority—
Returnsuint256
Wheresrc/Strategy.sol:7
1 argument(s)
whoaddress

IRouter src/Strategy

Signatories—
Wheresrc/Strategy.sol:10

IRouter.swap changes it declared only

Authority—
Returnsuint256
Wheresrc/Strategy.sol:11
3 argument(s)
tokenInaddress
tokenOutaddress
amountInuint256

IStrategy src/Strategy

Signatories—
Wheresrc/Strategy.sol:14

IStrategy.invest changes it declared only

Authority—
Wheresrc/Strategy.sol:15
1 argument(s)
amountuint256

IStrategy.withdraw changes it declared only

Authority—
Wheresrc/Strategy.sol:16
1 argument(s)
amountuint256

IStrategy.harvest changes it declared only

Authority—
Returnsuint256
Wheresrc/Strategy.sol:17

Strategy src/Strategy

Signatories—
Wheresrc/Strategy.sol:20

Strategy.constructor changes it runs once at deployment

Authority—
Wheresrc/Strategy.sol:26
4 argument(s)
_vaultaddress
_assetIERC20
_rewardIERC20
_routerIRouter
source
    constructor(address _vault, IERC20 _asset, IERC20 _reward, IRouter _router) {
        vault = _vault; asset = _asset; reward = _reward; router = _router;
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Strategy.invest changes it one principal

Authorityvault
Wheresrc/Strategy.sol:30
1 argument(s)
uint256
1 guard(s)
_onlyVault() → require(msg.sender == vault, "not vault");
source
    function invest(uint256) external { _onlyVault(); }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Strategy.withdraw changes it one principal

Authorityvault
Wheresrc/Strategy.sol:32
1 argument(s)
amountuint256
1 guard(s)
_onlyVault() → require(msg.sender == vault, "not vault");
1 edge(s)
call IERC20 asset.transfer(vault, amount);
source
    function withdraw(uint256 amount) external {
        _onlyVault();
        asset.transfer(vault, amount);
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Strategy.harvest changes it one principal

Authorityvault
Returnsuint256
Wheresrc/Strategy.sol:37
1 guard(s)
_onlyVault() → require(msg.sender == vault, "not vault");
3 edge(s)
call IERC20 uint256 earned = reward.balanceOf(address(this));
call IRouter gained = router.swap(address(reward), address(asset), earned);
call IERC20 asset.transfer(vault, gained);
source
    function harvest() external returns (uint256 gained) {
        _onlyVault();
        uint256 earned = reward.balanceOf(address(this));
        gained = router.swap(address(reward), address(asset), earned);
        asset.transfer(vault, gained);
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Strategy.emergencyExit changes it one principal

Authorityvault
Wheresrc/Strategy.sol:44
1 guard(s)
if (msg.sender != vault) revert();
1 edge(s)
call IERC20 asset.transfer(vault, asset.balanceOf(address(this)));
source
    function emergencyExit() external {
        if (msg.sender != vault) revert();
        asset.transfer(vault, asset.balanceOf(address(this)));
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Owned src/Vault

Signatories—
Wheresrc/Vault.sol:7

Owned.transferOwnership changes it one principal

AuthorityonlyOwner
Wheresrc/Vault.sol:10
1 argument(s)
toaddress
source
    function transferOwnership(address to) external onlyOwner { owner = to; }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault src/Vault

Signatories—
Wheresrc/Vault.sol:13

Vault.constructor changes it runs once at deployment

Authority—
Wheresrc/Vault.sol:20
1 argument(s)
_assetIERC20
source
    constructor(IERC20 _asset) { asset = _asset; owner = msg.sender; }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.deposit changes it

Authority—
Wheresrc/Vault.sol:22
1 argument(s)
amountuint256
4 edge(s)
call IERC20 asset.transferFrom(msg.sender, address(this), amount);
call IERC20 via _invest uint256 idle = asset.balanceOf(address(this));
call IERC20 via _invest asset.transfer(address(strategy), idle);
call IStrategy via _invest strategy.invest(idle);
source
    function deposit(uint256 amount) external {
        asset.transferFrom(msg.sender, address(this), amount);
        shares[msg.sender] += amount;
        _invest();
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.withdraw changes it

Authority—
Wheresrc/Vault.sol:28
1 argument(s)
amountuint256
2 edge(s)
call IStrategy strategy.withdraw(amount);
call IERC20 asset.transfer(msg.sender, amount);
source
    function withdraw(uint256 amount) external {
        shares[msg.sender] -= amount;
        strategy.withdraw(amount);
        asset.transfer(msg.sender, amount);
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.harvest changes it one principal

Authoritykeepers[msg.sender]
Wheresrc/Vault.sol:34
1 guard(s)
_onlyKeeper() → require(keepers[msg.sender], "not keeper");
5 edge(s)
call IStrategy uint256 profit = strategy.harvest();
call IERC20 via _takeFee asset.transfer(owner, amount * feeBps / 10_000);
call IERC20 via _invest uint256 idle = asset.balanceOf(address(this));
call IERC20 via _invest asset.transfer(address(strategy), idle);
call IStrategy via _invest strategy.invest(idle);
source
    function harvest() external {
        _onlyKeeper();
        uint256 profit = strategy.harvest();
        _takeFee(profit);
        _invest();
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.takeFee changes it

Authority—
Wheresrc/Vault.sol:41
1 argument(s)
amountuint256
1 edge(s)
call IERC20 via _takeFee asset.transfer(owner, amount * feeBps / 10_000);
source
    function takeFee(uint256 amount) external { _takeFee(amount); }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.setStrategy changes it one principal

AuthorityonlyOwner
Wheresrc/Vault.sol:43
1 argument(s)
nextIStrategy
source
    function setStrategy(IStrategy next) external onlyOwner { strategy = next; }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.setKeeper changes it one principal

AuthorityonlyOwner
Wheresrc/Vault.sol:44
2 argument(s)
keeperaddress
allowedbool
source
    function setKeeper(address keeper, bool allowed) external onlyOwner { keepers[keeper] = allowed; }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.

Vault.execute changes it one principal

AuthorityonlyOwner
Returnsbytes
Wheresrc/Vault.sol:46
2 argument(s)
targetaddress
databytes
1 guard(s)
require(ok, "call failed");
1 edge(s) — 1 unresolved
call could not determine (bool ok, bytes memory out) = target.delegatecall(data);
source
    function execute(address target, bytes calldata data) external onlyOwner returns (bytes memory) {
        (bool ok, bytes memory out) = target.delegatecall(data);
        require(ok, "call failed");
        return out;
    }

Shown with comments intact. Comments are stripped before parsing and were never used to produce anything above.