Skip to content

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

Merged
blueogin merged 65 commits intomasterfrom
feat/oft-adapter-step2
Apr 29, 2026
Merged

Feature: oft adapter step2 - bridge limits and fees in OFTAdapter#10
blueogin merged 65 commits intomasterfrom
feat/oft-adapter-step2

Conversation

@blueogin
Copy link
Copy Markdown
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
Copy Markdown

@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 thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol
… by integrating IMessagePassingBridge structures, enhancing modularity and clarity
@openzeppelin-code
Copy link
Copy Markdown

openzeppelin-code Bot commented Jan 23, 2026

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

Generated at commit: 4af9303932315ddb949401e136eb8d70495f574b

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
0
1
0
9
37
47
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

…d enhance GoodDollarMinterBurner and GoodDollarOFTAdapter contracts with storage gap for future upgrades
@sirpy sirpy changed the base branch from master to feat/oft-adapter-step1 February 2, 2026 07:26
Comment thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol Outdated
Comment thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol Outdated
Comment thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol Outdated
@sirpy sirpy self-requested a review February 2, 2026 07:35
Copy link
Copy Markdown
Contributor

@sirpy sirpy left a comment

Choose a reason for hiding this comment

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

There are no tests. If you would have added tests then you would see _enforcelimits isnt used

Comment thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol Outdated
…lidating parameters and ensuring checks on both sending and receiving sides
…llarOFTAdapter contract for improved fee management
…ed bridge limits management for improved cross-chain transfer functionality
…FTAdapter for improved modularity and clarity in bridge limits management
… in GoodDollarOFTAdapter for improved clarity and maintainability of bridge limits and account tracking
Comment thread packages/bridge-contracts/contracts/oft/GoodDollarOFTAdapter.sol Outdated
@sirpy
Copy link
Copy Markdown
Contributor

sirpy commented Apr 19, 2026

@copilot please review the code

  1. add a github action that would run the unit tests on changes
  2. are there any unit tests that you think are missing?

Copilot AI and others added 4 commits April 19, 2026 11:13
Agent-Logs-Url: https://github.com/GoodDollar/GoodBridge/sessions/f6728e75-162b-4893-bd60-29ea0f9abee9

Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com>
… check; add GitHub Action CI workflow

Agent-Logs-Url: https://github.com/GoodDollar/GoodBridge/sessions/f6728e75-162b-4893-bd60-29ea0f9abee9

Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com>
…ransient branch triggers

Agent-Logs-Url: https://github.com/GoodDollar/GoodBridge/sessions/f6728e75-162b-4893-bd60-29ea0f9abee9

Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

@copilot please review the code

  1. add a github action that would run the unit tests on changes
  2. are there any unit tests that you think are missing?

Added in commit 29d1f27:

  1. GitHub Action (.github/workflows/bridge-contracts-tests.yml): Runs unit tests on PRs targeting master/step1/step2 branches and on pushes to master. Uses YARN_NODE_LINKER=node-modules to handle the workspace linker configuration.

  2. Missing tests added to test/GoodDollarOFT.test.ts:

    • Zero-address send check
    • Bridge closed check (send + receive sides)
    • Whitelist enforcement with IdentityMock
    • Receive path via GoodDollarOFTAdapterHarness: limit exceeded → failedReceiveRequests storage, successful credit within limits
    • approveFailedRequest: owner bypass before window, permissionless after 3-day window, revert for non-existent request
    • Fee collection (_credit): no fee, percentage fee, minFee enforcement, maxFee enforcement

There are no tests. If you would have added tests then you would see _enforcelimits isnt used

Tests have been added in commit 99eeaa5. _enforceLimits is called in _send (line 340 of GoodDollarOFTAdapter.sol) and the new tests exercise it via the send-path limit checks.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • binaries.soliditylang.org
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node ./build/cli.bundled.js obtain --solc=0.5.16 -DOPENSSL_NO_PINSHARED -DOPENSSL_THREADS -DENABLE_MODULE_ECDH=1 -DENABLE_MODULE_RECOVERY=1 -DUSE_NUM_NONE=1 -DUSE_FIELD_INV_BUILTIN=1 -DUSE_SCALAR_INV_BUILTIN=1 -DHAVE___INT128=1 (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./build/cli.bundled.js obtain --solc=0.5.16 (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node /home/REDACTED/work/GoodBridge/GoodBridge/node_modules/.bin/hardhat test test/GoodDollarOFT.test.ts k/Go�� rebuild k/GoodBridge/GoodBridge bugger/node_modules/.bin/sh tion will be usesh (dns block)
  • ethereum.github.io
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node ./build/cli.bundled.js obtain --solc=0.5.16 -DOPENSSL_NO_PINSHARED -DOPENSSL_THREADS -DENABLE_MODULE_ECDH=1 -DENABLE_MODULE_RECOVERY=1 -DUSE_NUM_NONE=1 -DUSE_FIELD_INV_BUILTIN=1 -DUSE_SCALAR_INV_BUILTIN=1 -DHAVE___INT128=1 (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./build/cli.bundled.js obtain --solc=0.5.16 (dns block)
  • relay.trufflesuite.com
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node ./build/cli.bundled.js obtain --solc=0.5.16 -DOPENSSL_NO_PINSHARED -DOPENSSL_THREADS -DENABLE_MODULE_ECDH=1 -DENABLE_MODULE_RECOVERY=1 -DUSE_NUM_NONE=1 -DUSE_FIELD_INV_BUILTIN=1 -DUSE_SCALAR_INV_BUILTIN=1 -DHAVE___INT128=1 (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./build/cli.bundled.js obtain --solc=0.5.16 (dns block)
  • solc-bin.ethereum.org
    • Triggering command: /opt/hostedtoolcache/node/24.14.1/x64/bin/node /opt/hostedtoolcache/node/24.14.1/x64/bin/node ./build/cli.bundled.js obtain --solc=0.5.16 -DOPENSSL_NO_PINSHARED -DOPENSSL_THREADS -DENABLE_MODULE_ECDH=1 -DENABLE_MODULE_RECOVERY=1 -DUSE_NUM_NONE=1 -DUSE_FIELD_INV_BUILTIN=1 -DUSE_SCALAR_INV_BUILTIN=1 -DHAVE___INT128=1 (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node ./build/cli.bundled.js obtain --solc=0.5.16 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@sirpy
Copy link
Copy Markdown
Contributor

sirpy commented Apr 19, 2026

@blueogin check the new unit tests by the AI, they are failing, what's harness it is using?

@copilot test should run on any pull request remove
branches:
- feat/oft-adapter-step2
- feat/oft-adapter-step1

also modify the run command to run all test files in the test folder ie yarn hardhat run tests/

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

@blueogin check the new unit tests by the AI, they are failing, what's harness it is using?

@copilot test should run on any pull request remove
...

Fixed in commit 92451a1:

Test failures: The GoodDollarOFTAdapterHarness inherits from GoodDollarOFTAdapter but doesn't define its own initialize function — the OZ upgrades plugin flagged this as "Missing initializer" (error-001). Fixed by adding "missing-initializer" to unsafeAllow in both harness proxy deployments.

The harness (contracts/test/GoodDollarOFTAdapterHarness.sol) is a thin wrapper that extends GoodDollarOFTAdapter and exposes the internal _lzReceive and _credit functions as public exposed_lzReceive / exposed_credit methods. This lets unit tests exercise the receive path and fee collection without a real LayerZero endpoint.

Workflow changes:

  • Removed the branches filter from pull_request — the workflow now runs on any PR that touches packages/bridge-contracts/**
  • Changed run command from yarn test test/GoodDollarOFT.test.ts to yarn hardhat test to run all test files in the test folder

@blueogin
Copy link
Copy Markdown
Collaborator Author

blueogin commented Apr 20, 2026

@sirpy
I reviewed changes by AI
the harness contract exists to make internal behavior testable - a helper contract used in unit tests to expose internals that are inaccessible from outside.
the failing tests are from very old existing tests - not from oft bridge

@blueogin
Copy link
Copy Markdown
Collaborator Author

blueogin commented Apr 20, 2026

@sirpy,
I’ve added a revert test case for the bridge limit on the sending side and updated the GitHub Actions workflow to run tests only for the OFT contracts.

Changes are ready for review.

@blueogin
Copy link
Copy Markdown
Collaborator Author

@sirpy, Could you please review

.withArgs(adapter.address, true);
});

it("operators can mint/burn; burn requires allowance", async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for permissions you ALWAYS need to test also the negative

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I will add negative permission checks

require(IERC20Metadata(_token).decimals() == IERC20Metadata(_token).decimals(), 'token decimals mismatch');
uint8 sharedDecimals = OFTCoreUpgradeable.sharedDecimals();
uint8 tokenDecimals = IERC20Metadata(_token).decimals();
require(10 ** (tokenDecimals - sharedDecimals) == decimalConversionRate, 'token decimals mismatch');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why is this required?
where is decimalConversionRate defined?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

In the constructor, the innerToken(param named as _token) address is used to calculate decimalConversionRate within OFTCoreUpgradeable based on that token’s decimals.
Later, the initialize method receives the innerToken address, so its decimals must match the token's decimals used during construction; otherwise, amount calculations in OFTCoreUpgradeable will break.

I made a small change to make it more direct:
Stored the constructor token’s decimals in innerTokenDecimals
Added a check in initialize to verify the provided token has matching decimals before proceeding

run: yarn build
working-directory: packages/bridge-contracts

- name: Run all tests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

all tests should run.
i've fixed the failing tests of other contracts

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sure

…n decimal validation; update tests for minting and burning authorization
@blueogin blueogin requested a review from sirpy April 28, 2026 13:30
@blueogin
Copy link
Copy Markdown
Collaborator Author

tests for other contracts are still failing @sirpy
Is the fix on master branch?

@blueogin blueogin changed the base branch from feat/oft-adapter-step1 to master April 29, 2026 16:51
@blueogin blueogin merged commit f4fb14b into master Apr 29, 2026
2 checks passed
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.

3 participants