Organization
- @OpenTrade
Engagement Type
Cantina Reviews
Period
-
Researchers
Findings
Low Risk
2 findings
2 fixed
0 acknowledged
Informational
2 findings
1 fixed
1 acknowledged
Low Risk2 findings
setIssuerAdmin and service configuration can be out of sync
Severity
- Severity: Low
Submitted by
Alireza Arjmand
Description
setIssuerAdminvalidatesISSUER_ADMIN_ROLEagainst the service configuration but persists the chosen address locally in the controller. Since the check only runs at write time, the two can drift apart: once an address is granted the role and set here, later revoking it in the configuration has no effect on the controller. There is also no path to clear_issuerAdminfrom the controller, so once set it cannot return to the fallback unless a new valid holder is assigned.Recommendation
Re-validate the role at the point of use (or on read) rather than only at write time, and add a way to clear
_issuerAdminback to the issuer fallback.OpenTrade: addressed in the
issuerAdminfunction check https://github.com/opentrade-io/LytPool/commit/26e6ed9353c0d2f2c94b23c3350d35180fa8be77Cantina Managed: Verified fixes,
issuerAdmin()now re-validates the role on every read andclearIssuerAdmin()restores the issuer fallback.Tests never use a real issuer admin
Severity
- Severity: Low
Submitted by
Alireza Arjmand
Description
Every test exercises the
issuerAdmin == issuerfallback;setIssuerAdminis never called, so a diverged issuer admin — the core behavior this change introduces, is never covered. The suite would pass unchanged if deposits were still gated byonlyIssuer.Recommendation
Add coverage for
issuerAdmin != issuer:- after
setIssuerAdmin(newAdmin), the new admin can deposit and the previous issuer is rejected withNotIssuerAdmin setIssuerAdminreverts for a non–protocol-admin caller, while paused, and for a target that doesn't holdISSUER_ADMIN_ROLE- an operator without
PAUSER_ROLEcannot callsetPaused
OpenTrade: Addressed in commit https://github.com/opentrade-io/LytPool/commit/26e6ed9353c0d2f2c94b23c3350d35180fa8be77
Cantina Managed: Verified fixes, the new tests cover the diverged issuer admin, all
setIssuerAdminrevert paths, and the operator-without-PAUSER_ROLE case.
Informational4 findings
Review Scope
State
- New
Severity
- Severity: Informational
≈
Likelihood: Low×
Impact: Low Submitted by
Alireza Arjmand
Scope
Commit
199ec6bon branchfeat/pauser-issuer-admin-role.The assumption of this review is that the already existing code is working correctly and mechanisms such as upgrade are running with no issues. The only reviewed changes are the ones that are introduced in the commit
199ec6b.Files changed under
contracts/:contracts├── LYTPool.sol├── LYTPoolServiceConfiguration.sol├── controllers│ ├── LYTPoolController.sol│ └── interfaces│ └── ILYTPoolController.sol└── interfaces └── ILYTPoolServiceConfiguration.solCode Overview
State
- New
Severity
- Severity: Informational
Submitted by
Alireza Arjmand
Code overview
Commit
199ec6badds a pauser role and a per-pool issuer admin, then re-gates who can call which functionality.LYTPoolServiceConfiguration: newPAUSER_ROLEandISSUER_ADMIN_ROLEconstants, anonlyPausermodifier, and anisIssuerAdmin()view.setPausedmoves fromonlyOperatortoonlyPauser.LYTPoolController: new per-pool_issuerAdminwith anissuerAdmin()getter that falls back to the issuer when it's unset. AddssetIssuerAdmin()(protocol admin only, target must holdISSUER_ADMIN_ROLE), anonlyIssuerAdminmodifier, and_isIssuerAdmin().onlyIssuerOrAutomationnow checks the issuer admin instead of the issuer.setFeeCollectorAddress,setTreasuryWalletAddrandaddAdjustmentAmountmove fromonlyIssuertoonlyIssuerAdmin;setIndicativeAndCollateralRatesmoves fromonlyIssuertoonlyIssuerOrAutomation.LYTPool: newonlyIssuerAdminmodifier that readscontroller.issuerAdmin(), put onchangeRedemptionDestination,depositOffChainanddepositFromTransfer(wereonlyIssuer).- Interfaces:
issuerAdmin()added toILYTPoolController,isIssuerAdmin()toILYTPoolServiceConfiguration.
In the above changes, the team has informed Cantina that the
IssuerAdminrole is most likely (but not necessarily) is going to be a timelock.Same role pauses and unpauses
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Alireza Arjmand
Description
setPausedgates both pausing and unpausing behind a singlePAUSER_ROLE. Now that this is no longer tied to operators, the two actions don't warrant the same trust: pausing is an emergency action that benefits from broad, fast access, while unpausing is deliberate and should be more tightly held.Recommendation
Consider separating into two different roles. One role as broader role to pause, and a narrower one to unpause.
OpenTrade: This was brought up to the team and requires a larger discussion. For now, we will probably defer this suggestion to a later date.
Cantina Managed: Acknowledged, deferred by the team;
setPausedremains gated by the singlePAUSER_ROLE.Batched nits in the codebase
Severity
- Severity: Informational
Submitted by
Alireza Arjmand
Below are smaller issues that are batched up together as a list of small nit issues throughout the codebase:
PAUSER_ROLEis declared apart from the other role constants:LYTPoolServiceConfiguration.sol#L41. Move it up beside the others for consistency.- No
isPauserview:LYTPoolServiceConfiguration.sol#L143. Every other role exposes anis*getter; addisPauser(address). _issuerAdminis a misleading name:LYTPoolController.sol#L67. With an existing issuer role, this reads as that role's admin. Rename it to something clearer.onlyIssuerOrAutomationno longer matches its check:LYTPoolController.sol#L104. It now checks the issuer admin, so rename it toonlyIssuerAdminOrAutomation.onlyIssueris unused:LYTPoolController.sol#L131. After the re-gating,onlyIssueris no longer applied to any functions in the controller, consider removing it.
OpenTrade: addressed in commit https://github.com/opentrade-io/LytPool/commit/26e6ed9353c0d2f2c94b23c3350d35180fa8be77
Cantina Managed: Verified fixes, all five nits are addressed.