Skip to content

feat(EXSC-408): S11 — access-control interfaces + reference adapter#1981

Draft
gvladika wants to merge 35 commits into
dev-vault-wrapperfrom
feature/exsc-408-s11-access-interfaces-reference-adapter
Draft

feat(EXSC-408): S11 — access-control interfaces + reference adapter#1981
gvladika wants to merge 35 commits into
dev-vault-wrapperfrom
feature/exsc-408-s11-access-interfaces-reference-adapter

Conversation

@gvladika

Copy link
Copy Markdown
Contributor

Which Linear task belongs to this PR?

EXSC-408 — S11: Access interfaces + reference adapter

Stacked PR. Base is feature/exsc-409-s1-core-erc-4626-immutable-args-cwia (#1971, S1). Review the incremental diff against the base; do not merge before S1 lands. First link in the stack S1 → S11 → S5 → S3.

Why did I implement it this way?

S11 is foundational and dependency-free: it ships only the external access-control surface (interfaces + a reference adapter) that S4's in-wrapper backends will import. It does not touch the wrapper — _checkAccess stays a no-op seam until S4.

  • IVaultAccessControl (isAllowed/isDenied), not IAccessControl. The ticket names it IAccessControl, but this subsystem now vendors OZ v5, whose @openzeppelin/contracts/access/IAccessControl.sol (role-based: hasRole/grantRole) would hard-clash on the identifier the moment any S4 file imports both. Renamed to avoid that; the methods (isAllowed/isDenied) are unrelated to OZ's role model anyway. Two independent predicates so one adapter serves allowlist, denylist, or both modes.
  • ISanctionsOracle matches Chainalysis verbatim. isSanctioned(address) → bool is the exact SanctionsList signature, so a live Chainalysis oracle drops in as the screening source with no shim.
  • ReferenceAccessControl is a template, not a LI.FI-operated contract. Each integrator deploys/forks its own. TransferrableOwnership-gated allowlist + denylist + optional sanctions oracle. Policy: denied = denylisted OR sanctioned; allowed = !denied && (!allowlistEnabled || allowlisted). Single + batch setters; the oracle is called directly (no try/catch) because ISanctionsOracle contractually must not revert — a flaky oracle is the integrator's adapter choice, documented in NatSpec. Holds no funds.

Checklist before requesting a review

  • I have performed a self-review of my code
  • This pull request is as small as possible and only tackles one problem
  • I have run /pr-ready (local CodeRabbit) on this branch and resolved (or explicitly documented) all findings — see .agents/commands/pr-ready.md
  • I have added tests that cover the functionality / test the bug
  • For new facets: I have checked all points from this list: https://www.notion.so/lifi/New-Facet-Contract-Checklist-157f0ff14ac78095a2b8f999d655622e
  • I have updated any required documentation

Checklist for reviewer (DO NOT DEPLOY and contracts BEFORE CHECKING THIS!!!)

  • I have checked that any arbitrary calls to external contracts are validated and or restricted
  • I have checked that any privileged calls (i.e. storage modifications) are validated and or restricted
  • I have ensured that any new contracts have had AT A MINIMUM 1 preliminary audit conducted on by <company/auditor>

gvladika and others added 20 commits June 23, 2026 15:02
…system constraints)

Encode the Diamond/Vault-Wrapper product boundary in the agent harness:

- 002-architecture.md (always-apply): add [CONV:ARCH-VAULTWRAPPER] so the
  boundary fires unconditionally every session, correcting the rule's
  Diamond-only framing.
- 108-vault-wrapper.md (new, scoped): operational constraints that activate
  on src/VaultWrapper/**, test/solidity/VaultWrapper/**, and the vault-wrapper
  deploy scripts. The deploy-script glob makes the rule fire at the decision
  point where the load-bearing script/deploy/facets/ location matters.

Product boundary lives in always-apply 002; operational detail in the scoped
rule (no duplication). Volatile state stays in Linear.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ultWrapper/

Reflect the S8 decision (#1936) to move the vault-wrapper deploy script out of
the Diamond-era script/deploy/facets/ lookup dir into its own folder. Flip the
[CONV:VW-DEPLOY-DIR] guidance: scripts are standalone, run by explicit path, and
deliberately decoupled from the Diamond deploy tooling — not kept in facets/.
Update the activation glob from facets/ to script/deploy/vaultWrapper/**.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add LiFiVaultWrapper, the Solady ERC-4626 beacon implementation behind the
factory: write-once initialize, virtual-share inflation mitigation, reentrancy
guards, and deposit/withdraw routing to the underlying yield source via an
IYieldAdapter delegatecall (the read path stays a plain view call).

- Extend IYieldAdapter with deposit/withdraw (delegatecall-only, stateless) and
  totalAssets(underlying, holder); implement them in ERC4626Adapter.
- Capture the deploying factory write-once for the later global circuit breaker.
- Replace the temporary MockVaultWrapper: the factory, beacon, and timelock
  tests now exercise the real implementation; only the underlying is mocked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…FiVaultWrapper.sol:initialize)

CodeRabbit: initialize had no event for off-chain indexing of an instance's
config. Emit Initialized(asset, underlying, adapter, vaultWrapperAdmin, factory,
integratorShareBps) and cover it with a vm.expectEmit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pper.sol:feeRate)

CodeRabbit: feeRate/feeEnabled indexed the [4] arrays by _feeType with only
Solidity's implicit out-of-bounds panic. Add an explicit InvalidFeeType(uint8)
revert making the valid range (0-3) explicit, with a revert test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each instance now reports a distinct ERC20 name/symbol composed from the
underlying asset symbol ("LI.FI Earn USDC" / "lfUSDC") instead of a shared
generic string, so wallets and explorers can tell instances apart. Reads the
symbol via Solady MetadataReaderLib (handles string/bytes32/missing) and falls
back to "VW" when the asset exposes no symbol.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the upcoming-feature call sites now so follow-up tickets only fill in
bodies. Adds a _beforeOperation() guard (pause -> access -> fee accrual) at the
top of deposit/mint/withdraw/redeem, overrides the four preview functions to
route through _entryFee/_exitFee, and skims/routes fees in the deposit/withdraw
hooks via _routeFee. All stubs are no-ops (fees return 0, guards pass), so
behaviour is unchanged and the suite stays green; bodies land in the fee (S2),
access (S4), and pause (S5) tickets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the Solady ERC4626 base with OpenZeppelin's ERC4626Upgradeable
(v4.9.2, matching the vendored openzeppelin-contracts). Source-verified survey
of major vault protocols showed no production use of Solady's ERC4626 and no
documented standalone audit of it, whereas OZ's base has broad precedent
(MetaMorpho, Gearbox, Ethena) — and Kiln DeFi, the closest comparable
(integrator-facing beacon-proxy vaults with delegatecall connectors + per-vault
fees), is built on ERC4626Upgradeable.

Changes:
- Add openzeppelin-contracts-upgradeable submodule (v4.9.2) + remappings; switch
  the bare @openzeppelin/ remapping to explicit @openzeppelin/contracts/ and
  @openzeppelin/contracts-upgradeable/ prefixes to avoid the auto-detect clash.
- asset/decimals/name/symbol now live in OZ proxy storage via __ERC4626_init /
  __ERC20_init (per-instance name/symbol still derived from the asset symbol).
- initialize uses OZ's initializer guard; adapter routing moves into _deposit /
  _withdraw overrides (OZ has no after/before hooks); reentrancy via
  ReentrancyGuardUpgradeable.
- Unchanged: delegatecall IYieldAdapter, no-op fee/pause/access seams,
  fee-adjusted preview overrides, factory and ILiFiVaultWrapper interface.

Inflation protection now relies on OZ's virtual-share offset (default +1).
All 92 vault-wrapper tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…:278)

CR (minor): a delegatecalled adapter runs in this wrapper's storage context, so
"stateless, cannot touch storage" is misleading — statelessness is an
audit/governance invariant, not enforced by this contract. Reworded accordingly.
…pper.sol:29)

CR (major): the proxy initializer is guarded but the implementation itself could
be initialized directly. Add a constructor calling _disableInitializers() (OZ
best practice, matches Kiln). Rework the unit-test harness to deploy the impl
behind an UpgradeableBeacon + BeaconProxy so initialize is still exercised
end-to-end through a proxy.
CR (major): a single-shot initializer should not rely solely on upstream factory
validation. Reject zero asset/underlying/adapter/admin, integratorShareBps over
10000, and an adapter that resolves the underlying to an asset other than the one
passed (defense-in-depth against bricking/mispricing). Add testRevert_ coverage
for each path. Redundant in the factory flow but cheap insurance on a fund-custody
contract.
CR (major): the prior override pulled from the yield source before super._withdraw
spent the allowance and burned shares. Inline OZ's sequence so checks/effects
(spend allowance, burn) precede the adapter interaction and the asset transfer,
satisfying Checks-Effects-Interactions instead of relying solely on nonReentrant.
# Conflicts:
#	script/deploy/vaultWrapper/DeployLiFiVaultWrapperFactory.s.sol
#	src/VaultWrapper/adapters/ERC4626Adapter.sol
#	src/VaultWrapper/mocks/MockVaultWrapper.sol
Move all VaultWrapper code (wrapper, adapter, deploy script, tests) onto
OpenZeppelin v5.6.1 while the Diamond stays on the vendored OZ v4.9.2.

- Vendor a second OZ core submodule (lib/openzeppelin-contracts-v5 @ v5.6.1)
  because v5's upgradeable package imports the non-upgradeable core as a peer.
- Bump lib/openzeppelin-contracts-upgradeable v4.9.2 -> v5.6.1.
- Route VaultWrapper src/test/script and the upgradeable lib to v5 core via
  path-scoped remappings; the global @openzeppelin/contracts/ stays v4.9.2.
- LiFiVaultWrapper: ReentrancyGuardUpgradeable -> core ReentrancyGuard (v5 is
  proxy-safe, no __init), IERC20Upgradeable/SafeERC20Upgradeable -> IERC20/
  SafeERC20; extract _initErc4626Metadata to avoid v5 stack-too-deep.
- Deploy script: v5 UpgradeableBeacon(impl, initialOwner) sets the timelock as
  owner at construction.
- Tests: v5 custom errors (InvalidInitialization, ReentrancyGuardReentrantCall,
  OwnableUnauthorizedAccount, BeaconInvalidImplementation,
  TimelockUnexpectedOperationState) and DEFAULT_ADMIN_ROLE.

97 VaultWrapper tests pass; full repo build green (Diamond unaffected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LiFiVaultWrapper and the deploy script are unreleased; revert the
@Custom:version churn (2.0.0 -> 1.0.0, 1.3.0 -> 1.2.0) so this PR
introduces no version change against its base.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add [CONV:VW-OZ-VERSION] (dual-OZ-version split + path-scoped remappings;
the high-value, silent-failure constraint) and note v5's two-arg
UpgradeableBeacon constructor in [CONV:VW-BEACON].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace Solady LibClone beacon-proxy deployment with OZ BeaconProxy +
Create2 so the VaultWrapper proxy layer is single-vendor OZ. A shared
_proxyInitCode() keeps deploy and predictAddress hashing identical init
code; salt reuse now reverts with OZ Errors.FailedDeployment. Solady
remains only for MetadataReaderLib (no OZ equivalent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
S11. IVaultAccessControl (isAllowed/isDenied) and ISanctionsOracle
(Chainalysis-compatible isSanctioned) interfaces, plus a reference
external access adapter combining an owner-managed allowlist/denylist
with an optional sanctions oracle. Integrator template; in-wrapper
Merkle/mapping backends remain S4.

Named IVaultAccessControl (not IAccessControl) to avoid colliding with
OZ v5's role-based IAccessControl vendored in this subsystem.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial review gaps: cover batch setters' onlyOwner guard, the
toggle-off (false) direction for allow/deny, per-element batch event
emission, empty-array batch no-op, and the allowlist-disable reopen
path. Add blank lines after vm.expectRevert per [CONV:BLANKLINES].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3cc3091e-5798-43f4-a0a7-8734d61e5d46

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/exsc-408-s11-access-interfaces-reference-adapter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
Comment thread src/VaultWrapper/access/ReferenceAccessControl.sol Dismissed
gvladika and others added 7 commits June 26, 2026 10:23
Two hardening fixes flagged by review of the S1 core:

1. Adapter return values were ignored and ERC4626Adapter echoed its input,
   so a fee-charging or short-paying yield source could silently dilute
   minted shares or let a withdrawal pull from the wrapper's buffer. The
   adapter now returns the actual asset balance delta, _routeThroughAdapter
   forwards it, and _deposit/_withdraw revert with AdapterDepositShortfall /
   AdapterWithdrawShortfall when the yield source moves less than requested.

2. Add a reserved __gap to the upgradeable beacon implementation so future
   versions can append wrapper state without shifting derived-module storage
   (append-only, no-reorder upgrade invariant documented inline).

Tests: a configurable LossyVault proves both shortfall guards fire; the
58/99 existing suites stay green (standard vaults move exact amounts).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address the review point that the balance-delta deposit measurement does
not catch share-side dilution (a vault that consumes the full asset but
credits fewer shares). Rather than switch to convertToAssets(shareDelta) —
which rounds below the requested amount on any non-1:1 vault and would
revert healthy deposits — keep the rounding-safe delta guard (which catches
short-accepting sources) and document that internal-deposit-fee /
fee-on-transfer sources are unsupported and need a dedicated adapter. Makes
the previously-silent 1:1 assumption explicit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
…1.0.0

Review follow-ups on the S1 PR:
- ERC4626Adapter: replace Solady SafeTransferLib.safeApproveWithRetry with
  OZ SafeERC20.forceApprove so the wrapper subsystem stays single-vendor OZ.
- Use named arguments on all IERC4626 calls (deposit/withdraw/convertToAssets,
  balanceOf).
- Reset in-development @Custom:version tags to 1.0.0 (adapter, IYieldAdapter,
  deploy script).
- 108-vault-wrapper rule: drop the stale CWIA parenthetical and the historical
  "do not reintroduce Solady LibClone" note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o (LiFiVaultWrapperFactory.sol:174,272)

CodeRabbit (major): the split validation allowed integratorShareBps == 100%,
leaving LI.FI 0% — violating rule 108-vault-wrapper line 72 ("the integrator/LI.FI
split is validated < 100% only"). Both the setDefaultSplit and deploy paths used
`> BPS_DENOMINATOR`; tightened to `>=` and aligned the NatSpec. Added boundary
tests asserting exactly 100% reverts and 99.99% is accepted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
Code-review follow-ups on the S1 PR:
- initialize: rejected only > 100% while the factory (and rule 108) enforce
  < 100%; aligned the wrapper's defense-in-depth check to >= 10_000 and assert
  exactly 100% reverts.
- _withdraw: stop reimplementing OZ's withdraw sequence; override only the
  _transferOut seam so OZ keeps the allowance spend, share burn, and Withdraw
  event. Behaviour is unchanged (redeem assets+fee, revert on shortfall before
  paying the receiver, skim fee, transfer assets) and it removes the asymmetry
  with _deposit plus the risk of drifting from OZ on upgrade.
- cache asset() in a local across the two reads in the withdraw path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gvladika and others added 2 commits June 26, 2026 14:05
- initialize no longer takes _asset: it derives the asset from _underlying via
  the adapter's resolveAsset, so the stored asset cannot disagree with what the
  adapter reports. Drops the redundant param and the now-impossible AssetMismatch
  error/check; the factory still resolves the asset for its WrapperDeployed event.
- vaultWrapperAdmin is now transferable via a two-step (propose/accept) flow,
  mirroring Ownable2Step, so the per-vault controller can be rotated without a
  redeploy and a mistyped address cannot strand the role. Reserved storage gap
  reduced 50 -> 49 to absorb the new pendingVaultWrapperAdmin slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
Comment thread src/VaultWrapper/LiFiVaultWrapper.sol Fixed
gvladika and others added 6 commits June 26, 2026 14:48
…LiFiVaultWrapper.sol:147)

CodeRabbit (major): removing _asset dropped the implicit zero-check the old
AssetMismatch validation provided. The reference adapter reverts on a zero
asset, but the wrapper should not trust an arbitrary governance-approved
adapter. Revert ZeroAddress when resolveAsset returns address(0); test via
MockZeroAdapter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
…trancyGuard

OZ v5.6.1 ships no ReentrancyGuardUpgradeable and ReentrancyGuardTransient
requires EIP-1153 on every target chain. Document that the plain guard is
proxy-safe here (the check treats the proxy's zero-initialized slot as
NOT_ENTERED) so the recurring review flag has a standing answer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
The earlier note claimed the guard occupies sequential slot 0; that is wrong.
OZ v5's ReentrancyGuard (v5.5.0+) stores its status in a fixed ERC-7201
namespaced slot via StorageSlot and is @Custom:stateless, so it occupies no
sequential slot (the wrapper's own `underlying` is slot 0) and is collision-free
behind a beacon proxy — which is why OZ retired the Upgradeable variant.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into feature/exsc-408-s11-access-interfaces-reference-adapter
Base automatically changed from feature/exsc-409-s1-core-erc-4626-immutable-args-cwia to dev-vault-wrapper June 26, 2026 13:37
@lifi-action-bot

lifi-action-bot commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

🤖 GitHub Action: Security Alerts Review 🔍

🟢 Dismissed Security Alerts with Comments
The following alerts were dismissed with proper comments:

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapper.sol
🔹 Using uninitialized state variables may lead to unexpected behavior. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/uninitialized-state-variable
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: Proxy state, not constructor-set. pendingVaultWrapperAdmin is deliberately zero until a two-step transfer starts; zero == no pending transfer, and acceptVaultWrapperAdmin() gates on msg.sender == pendingVaultWrapperAdmin so zero authorizes no one. Confirmed by review.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapper.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: initialize() is initializer-guarded (single-shot), called atomically by the factory; its only external call is the view resolveAsset() before the emit. No reentrancy vector, event ordering benign. Same pattern as the dismissed deploy() alert. Confirmed by security review.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Governance-gated onlyOwner config; owner is the 48h TimelockController per [CONV:ARCH-GOVERNANCE]. Owner control of privileged config is a required security control here, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Constructor: _owner is validated by TransferrableOwnership; a zero _sanctionsOracle is explicitly allowed (disables screening) and _allowlistEnabled is a bool. No validation needed. Same accepted convention as prior dismissals (alert 627).

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Constructor: _owner is validated by TransferrableOwnership; a zero _sanctionsOracle is explicitly allowed (disables screening) and _allowlistEnabled is a bool. No validation needed. Same accepted convention as prior dismissals (alert 627).

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/access/ReferenceAccessControl.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Owner-gated config on the reference access-control contract; the owner manages allow/deny lists by design. In production the owner is subsystem governance per [CONV:ARCH-GOVERNANCE]. A required control, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapper.sol
🔹 Allowing delegated calls to arbitrary addresses may result in execution of untrusted code. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/arbitrary-delegatecall
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: adapter is set write-once in initialize to a factory/governance-approved address with no post-init setter, so the delegatecall target is not arbitrary. The delegatecall adapter is the intended design ([CONV:VW-ADAPTERS]); statelessness enforced by review/audit.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapper.sol
🔹 Making an external call without a gas budget may consume all of the transaction's gas, causing it to revert. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/call-without-gas-budget
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: The governance-approved adapter must run the full deposit/withdraw into the yield source via delegatecall; the target is not attacker-controlled, so gas-griefing does not apply and full gas forwarding is intended. Capping gas would truncate legitimate yield-source interactions.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Governance-gated onlyOwner config; owner is the 48h TimelockController per [CONV:ARCH-GOVERNANCE]. Owner control of privileged config is a required security control here, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
🔹 Dismiss Reason: False positive
🔹 Dismiss Comment: deploy() has no reentrancy vector: only a view adapter staticcall, CREATE2 proxy creation (no user callback), and initialize() on the freshly-created clone, with isInstance written before init. Event ordering is benign. Confirmed by security review.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Governance-gated onlyOwner config; owner is the 48h TimelockController per [CONV:ARCH-GOVERNANCE]. Owner control of privileged config is a required security control here, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Governance-gated onlyOwner config; owner is the 48h TimelockController per [CONV:ARCH-GOVERNANCE]. Owner control of privileged config is a required security control here, not a vulnerability.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: By design: setters are gated to owner = LI.FI governance Timelock (48h) + multisig proposer, not an EOA. Single-owner admin gating is the intended trust boundary, mitigated by the timelock and timelock-gated role rotation (A19). EXSC-417.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: By design: setters are gated to owner = LI.FI governance Timelock (48h) + multisig proposer, not an EOA. Single-owner admin gating is the intended trust boundary, mitigated by the timelock and timelock-gated role rotation (A19). EXSC-417.

🟢 View Alert - File: src/VaultWrapper/LiFiVaultWrapperFactory.sol
🔹 Functions restricted to a single owner may result in loss or abuse of contract functionality if the owner's private key is compromised. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/owner-single-point-of-failure
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: By design: setters are gated to owner = LI.FI governance Timelock (48h) + multisig proposer, not an EOA. Single-owner admin gating is the intended trust boundary, mitigated by the timelock and timelock-gated role rotation (A19). EXSC-417.

🟢 View Alert - File: src/Helpers/TransferrableOwnership.sol
🔹 Parameters passed to a constructor that are not validated for correct values may lead to contract creation in an undesired state. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/no-parameter-validation-in-constructor
🔹 Dismiss Reason: Won't fix
🔹 Dismiss Comment: Since it’s a minor issue, we don’t need to validate the owner. Moreover, the contract can be marked as abstract. Newly added facets will generate similar alerts, and we will have the opportunity to validate the value before it reaches the parent contract.

No unresolved security alerts! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants