_replaceScope() validates each account in the new scope by calling an external contract. This is the correct approach for ensuring scope accounts are genuinely part of the underlying agreement IAgreement(agreement).isContractInScope(account) is the on-chain source of truth for account membership.
However, because the call happens inside a loop with no upper bound on iterations, the cumulative gas cost grows linearly with scope size. Each iteration makes an external STATICCALL/CALL to the agreement contract, meaning _replaceScope() becomes both gas-intensive and dependent on agreement liveness for every execution.
Likelihood:
Any pool covering an agreement with a moderately large number of BattleChain contracts (dozens or more) will encounter elevated gas costs on initialize() or setPoolScope(), with the block gas limit as an eventual hard ceiling.
If the agreement contract is paused, self-destructed, or its registry pointer migrated between scope updates, every isContractInScope() call reverts, permanently blocking scope changes without any on-chain recovery path.
Impact:
Pool creation (initialize()) can fail outright for agreements with large scopes, preventing sponsors from deploying confidence pools for those agreements at all.
setPoolScope() can become permanently uncallable if the agreement contract becomes unavailable, leaving the pool locked to its last-set scope with no way to correct it.
To demonstrate the gas exhaustion risk, consider a sponsor creating a pool for an agreement that covers 200 BattleChain contracts. Each call to agreement.isContractInScope(account) incurs a minimum CALL overhead plus the agreement-side execution cost. As the account count grows, the cumulative gas approaches and eventually exceeds the block gas limit, causing the transaction to revert, which means the pool can never be initialized for that agreement.
Add a MAX_SCOPE_SIZE constant and validate at the start of _replaceScope(). Also see L-02 for the unbounded array issue:
For larger agreements, consider accepting a sponsor-signed Merkle proof of membership instead of per-account external calls, reducing the on-chain validation to a single hash check.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.