Repository navigation
Fix the on-chain role-eligibility resolver checking the wrong account #171
Description
Difficulty: Advanced
Type: Bug / Security
Background: resolveChainRoleEligibility() in src/features/access/roleEligibilityResolver.ts receives a walletAddress parameter (the connected wallet whose role membership is being checked) and passes it through to buildRoleRequirementCallData — except it doesn't: buildRoleRequirementCallData(requirement: AccessRequirement) takes only the requirement, and internally builds the ABI call-data's hasRole(bytes32,address) second argument (addr) from requirement.address — the role/access-control contract's own address — not from the walletAddress parameter that resolveChainRoleEligibility receives but never forwards into the call-data builder at all.
Problem: The resulting eth_call effectively asks the contract "does this contract's own address hold this role" instead of "does the connected wallet hold this role" — the on-chain fallback path for multi-chain role eligibility is checking the wrong account on every single invocation. Depending on the target contract's own role assignments, this can produce either false negatives (a legitimate role-holder is reported as ineligible on this fallback path) or, in principle, false positives if the contract address itself happens to satisfy hasRole for reasons unrelated to the user — either way, the fallback resolver's output cannot be trusted for any wallet today.
Expected outcome: buildRoleRequirementCallData (or its caller) correctly encodes the connected wallet's address as the hasRole(bytes32,address) account argument, and the resolver's output accurately reflects the connected wallet's on-chain role status.
Suggested implementation: Change buildRoleRequirementCallData(requirement: AccessRequirement, walletAddress: string) to accept the wallet address explicitly, validate it with the same /^0x[a-fA-F0-9]{40}$/ pattern currently applied to requirement.address, and encode that into the call data's second argument instead of requirement.address; update resolveChainRoleEligibility's call site to pass walletAddress through. Add a regression test asserting the encoded call data's second 32-byte argument matches the connected wallet, not the requirement's contract address.
Acceptance criteria:
- A test constructs a
hasRolecall and asserts the ABI-encoded account argument equals the passed-inwalletAddress, notrequirement.address. - Existing tests in
tests/covering this resolver are updated to catch this class of regression (i.e., use awalletAddressandrequirement.addressthat are deliberately different values, so the bug would have been caught). - No change to the resolver's public function signatures beyond what's needed to thread
walletAddressthrough correctly.
Likely affected files: src/features/access/roleEligibilityResolver.ts, its test file(s).
Labels: bug, security, priority: high, GrantFox OSS, Maybe Rewarded, Official Campaign | FWC26
Activity
grantfox-oss commentedon Sep 14, 2026
🎉 This issue has been marked as completed on GrantFox!
@abojeEdwin's PR #301 was approved and merged by @Lakes41.
🏆 @abojeEdwin: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (468 total points). Track your full progress on GrantFox.
👏 Great work, @abojeEdwin! Keep contributing to Adamantine-guild.