Skip to content

Conversation

@blueogin
Copy link
Collaborator

Description

This PR inclues smart contracts which implementes bridge limits and fees in GoodDollarOFTAdapter.sol

About #7

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • PR title matches follow: (Feature|Bug|Chore) Task Name
  • My code follows the style guidelines of this project
  • I have followed all the instructions described in the initial task (check Definitions of Done)
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have added reference to a related issue in the repository
  • I have added a detailed description of the changes proposed in the pull request. I am as descriptive as possible, assisting reviewers as much as possible.
  • I have added screenshots related to my pull request (for frontend tasks)
  • I have pasted a gif showing the feature.
  • @mentions of the person or team responsible for reviewing proposed changes

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In GoodDollarOFTAdapter._credit the requestId is derived from block.timestamp, _to, _srcEid, and _amount, which makes it impossible to deterministically pre-approve off-chain (you can’t know the timestamp ahead of time); consider either passing a requestId from the message payload or changing the approval model so it’s usable in practice.
  • The BridgeFees struct includes minFee and maxFee, but _takeFee and _credit currently only use fee and ignore the min/max fields; either enforce these bounds in the fee calculation or remove the unused fields to avoid confusion.
  • The approvedRequests mapping in GoodDollarOFTAdapter is write-only and never cleared, so approved entries will accumulate indefinitely; consider adding a mechanism to delete or expire approvals after use to avoid unbounded storage growth.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `GoodDollarOFTAdapter._credit` the `requestId` is derived from `block.timestamp`, `_to`, `_srcEid`, and `_amount`, which makes it impossible to deterministically pre-approve off-chain (you can’t know the timestamp ahead of time); consider either passing a requestId from the message payload or changing the approval model so it’s usable in practice.
- The `BridgeFees` struct includes `minFee` and `maxFee`, but `_takeFee` and `_credit` currently only use `fee` and ignore the min/max fields; either enforce these bounds in the fee calculation or remove the unused fields to avoid confusion.
- The `approvedRequests` mapping in `GoodDollarOFTAdapter` is write-only and never cleared, so approved entries will accumulate indefinitely; consider adding a mechanism to delete or expire approvals after use to avoid unbounded storage growth.

## Individual Comments

### Comment 1
<location> `packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol:183-190` </location>
<code_context>
+     * @param amount The amount to calculate fee from
+     * @return fee The calculated fee amount
+     */
+    function _takeFee(uint256 amount) internal view returns (uint256 fee) {
+        fee = (amount * bridgeFees.fee) / 10000;
+    }
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Bridge fee calculation ignores `minFee`/`maxFee` fields of `BridgeFees`.

`BridgeFees` exposes `minFee` and `maxFee`, but `_takeFee` only uses the BPS value and never enforces these bounds. This can mislead integrators into assuming on-chain caps that don’t exist.

If these fields should be enforced, consider clamping the computed fee, e.g.:
```solidity
uint256 raw = (amount * bridgeFees.fee) / 10000;
if (bridgeFees.minFee > 0 && raw < bridgeFees.minFee) fee = bridgeFees.minFee;
else if (bridgeFees.maxFee > 0 && raw > bridgeFees.maxFee) fee = bridgeFees.maxFee;
else fee = raw;
```
If they’re informational only, consider renaming or removing them to avoid implying enforcement in this function.

```suggestion
    /**
     * @notice Calculates the fee amount from the given amount
     * @param amount The amount to calculate fee from
     * @return fee The calculated fee amount
     */
    function _takeFee(uint256 amount) internal view returns (uint256 fee) {
        uint256 raw = (amount * bridgeFees.fee) / 10000;
        uint256 minFee = bridgeFees.minFee;
        uint256 maxFee = bridgeFees.maxFee;

        if (minFee > 0 && raw < minFee) {
            fee = minFee;
        } else if (maxFee > 0 && raw > maxFee) {
            fee = maxFee;
        } else {
            fee = raw;
        }
    }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +183 to +190
/**
* @notice Calculates the fee amount from the given amount
* @param amount The amount to calculate fee from
* @return fee The calculated fee amount
*/
function _takeFee(uint256 amount) internal view returns (uint256 fee) {
fee = (amount * bridgeFees.fee) / 10000;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Bridge fee calculation ignores minFee/maxFee fields of BridgeFees.

BridgeFees exposes minFee and maxFee, but _takeFee only uses the BPS value and never enforces these bounds. This can mislead integrators into assuming on-chain caps that don’t exist.

If these fields should be enforced, consider clamping the computed fee, e.g.:

uint256 raw = (amount * bridgeFees.fee) / 10000;
if (bridgeFees.minFee > 0 && raw < bridgeFees.minFee) fee = bridgeFees.minFee;
else if (bridgeFees.maxFee > 0 && raw > bridgeFees.maxFee) fee = bridgeFees.maxFee;
else fee = raw;

If they’re informational only, consider renaming or removing them to avoid implying enforcement in this function.

Suggested change
/**
* @notice Calculates the fee amount from the given amount
* @param amount The amount to calculate fee from
* @return fee The calculated fee amount
*/
function _takeFee(uint256 amount) internal view returns (uint256 fee) {
fee = (amount * bridgeFees.fee) / 10000;
}
/**
* @notice Calculates the fee amount from the given amount
* @param amount The amount to calculate fee from
* @return fee The calculated fee amount
*/
function _takeFee(uint256 amount) internal view returns (uint256 fee) {
uint256 raw = (amount * bridgeFees.fee) / 10000;
uint256 minFee = bridgeFees.minFee;
uint256 maxFee = bridgeFees.maxFee;
if (minFee > 0 && raw < minFee) {
fee = minFee;
} else if (maxFee > 0 && raw > maxFee) {
fee = maxFee;
} else {
fee = raw;
}
}

… by integrating IMessagePassingBridge structures, enhancing modularity and clarity
@openzeppelin-code
Copy link

openzeppelin-code bot commented Jan 23, 2026

Feature: oft adapter step2 - bridge limits and fees in OFTAdapter

Generated at commit: 1949fc5e3af81e1d8fa51e35278957036c964e34

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
0
1
0
9
36
46
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

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.

2 participants