fix(FLOW-15): Return per-token balances in getPendingRequestsByUserUnpacked#36
fix(FLOW-15): Return per-token balances in getPendingRequestsByUserUnpacked#36
Conversation
…packed
Previously, `getPendingRequestsByUserUnpacked` only returned native FLOW
balances, ignoring ERC20/WFLOW balances. This made it impossible for
clients to see pending balances for non-native tokens.
Changes:
- Solidity: Return `balanceTokens[]`, `pendingBalances[]`, and
`claimableRefundsArray[]` instead of single uint256 values
- Cadence: Update `PendingRequestsInfo` struct to use dictionaries
`pendingBalances: {String: UFix64}` and `claimableRefunds: {String: UFix64}`
- Update ABI decoding to handle new array types
- Update all tests to use new return format
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request ReviewOverviewThis PR successfully addresses issue #30 by enhancing ✅ Strengths1. Clean API Design
2. Cross-VM Consistency
3. Test Coverage
4. Documentation
|
| var j = 0 | ||
| while j < balanceTokens.length { | ||
| let tokenAddr = balanceTokens[j].toString() | ||
| pendingBalances[tokenAddr] = FlowEVMBridgeUtils.uint256ToUFix64(value: pendingBalancesRaw[j], decimals: 18) |
There was a problem hiding this comment.
Hardcoded decimals: 18 will silently mis-represent any ERC-20 token that does not have 18 decimal places
Both lines here use decimals: 18:
pendingBalances[tokenAddr] = FlowEVMBridgeUtils.uint256ToUFix64(value: pendingBalancesRaw[j], decimals: 18)
claimableRefundsMap[tokenAddr] = FlowEVMBridgeUtils.uint256ToUFix64(value: claimableRefundsRaw[j], decimals: 18)NATIVE_FLOW and WFLOW both have 18 EVM decimals, so this PR is correct for the current token set. However, the Solidity side already has a generic setTokenConfig admin function that can add any ERC-20 token, and the balance arrays are keyed by token address — if a 6-decimal USDC or similar token were ever added, the Cadence side would silently report values ~10¹² times too small.
The safe fix is to use FlowEVMBridgeUtils.convertERC20AmountToCadenceAmount (which looks up the actual decimals via FlowEVMBridgeConfig) for non-native tokens, matching what ufix64FromUInt256 already does elsewhere in this contract. Alternatively, add a hard assertion that only NATIVE_FLOW/WFLOW (18-decimal) addresses can appear in this loop, so the assumption is at least explicit and checked.
| if (WFLOW != address(0)) { | ||
| balanceTokens[1] = WFLOW; | ||
| pendingBalances[1] = pendingUserBalances[user][WFLOW]; | ||
| claimableRefundsArray[1] = claimableRefunds[user][WFLOW]; |
There was a problem hiding this comment.
The WFLOW balance path (index [1]) has no test coverage — the core feature of this fix is untested
Every updated test assertion in this PR checks pendingBalances[0] / claimableRefundsArr[0] (NATIVE_FLOW). No test exercises:
balanceTokens[1] == WFLOWwhen WFLOW is configuredpendingBalances[1]reflecting an active WFLOW CREATE/DEPOSIT requestclaimableRefundsArr[1]reflecting a cancelled/dropped WFLOW request
A test along these lines is needed:
- Approve WFLOW, call
createYieldVault(WFLOW, 2 ether, ...)— verifypendingBalances[1] == 2 ether. - Cancel the request — verify
pendingBalances[1] == 0andclaimableRefundsArr[1] == 2 ether.
Without this test the regression this PR aims to fix (WFLOW balances not returned) could silently re-appear.
Review: fix(FLOW-15) — Return per-token balances in
|
| , | ||
| address[] memory balanceTokens, | ||
| uint256[] memory pendingBalances, | ||
| uint256[] memory claimableRefundsArr |
There was a problem hiding this comment.
The test setup now always configures WFLOW, so balanceTokens.length will be 2 here. The test only checks index 0 and silently drops claimableRefundsArray (trailing comma). Consider asserting balanceTokens.length == 2, pendingBalances[1] == 0 (no WFLOW pending), and that claimableRefundsArray[0] == 0 to make the return shape fully exercised for the WFLOW-configured case.
| uint256[] memory claimableRefundsArr | ||
| ) = c.getPendingRequestsByUserUnpacked(user); | ||
|
|
||
| assertEq(ids.length, 1, "User should have 1 pending request"); |
There was a problem hiding this comment.
There is no test covering the WFLOW = address(0) path. In that case tokenCount == 1 and callers receive a single-element array. Any client that hardcodes balanceTokens[1] (easy to write after reading the WFLOW example) will out-of-bounds revert on a deployment that has no WFLOW. A companion test that constructs the contract with WFLOW = address(0) and asserts balanceTokens.length == 1 would pin this invariant.
Summary
Fixes #30
Previously,
getPendingRequestsByUserUnpackedonly returned native FLOW balances, which made it harder for clients to surface per-token pending/refund balances.Note:
balanceTokens/pendingBalances/claimableRefundsArraycurrently coverNATIVE_FLOWand (when configured)WFLOWonly. Other supported ERC20 balances remain queryable viagetUserPendingBalance(user, token)andgetClaimableRefund(user, token).Changes:
balanceTokens[],pendingBalances[], andclaimableRefundsArray[]instead of single uint256 valuesPendingRequestsInfostruct to use dictionariespendingBalances: {String: UFix64}andclaimableRefunds: {String: UFix64}Test plan
🤖 Generated with Claude Code