Skip to content

✨ exa: add crosschain mint and burn support#797

Open
itofarina wants to merge 10 commits intomainfrom
exa-crosschain
Open

✨ exa: add crosschain mint and burn support#797
itofarina wants to merge 10 commits intomainfrom
exa-crosschain

Conversation

@itofarina
Copy link
Member

@itofarina itofarina commented Feb 25, 2026

Summary by CodeRabbit

  • New Features

    • EXA adds bridge-controlled cross-chain minting/burning and a v2 initialization path with expanded access control and interface support.
  • Tests

    • New comprehensive EXA test suite covering initialization, bridge access, mint/burn flows and interface support; many tests reorganized and gas snapshot metrics updated.
  • Chores

    • Dependency bumps (OpenZeppelin), CI/tooling updates, deployment timelock handling, and patch release metadata added.

Open with Devin

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: e02a973

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/protocol Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@gemini-code-assist
Copy link

Summary of Changes

Hello @itofarina, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the EXA token contract by incorporating ERC-7802 support, which standardizes cross-chain interactions for minting and burning tokens. The changes include a robust access control system to secure these operations, alongside essential dependency updates and a new, comprehensive test suite to ensure the reliability and correctness of the new features.

Highlights

  • ERC-7802 Support: The EXA token contract now implements the ERC-7802 standard, enabling standardized cross-chain minting and burning capabilities.
  • Access Control Integration: OpenZeppelin's AccessControlUpgradeable has been integrated into the EXA contract, introducing a BRIDGE_ROLE to manage permissions for cross-chain operations.
  • Dependency Updates: OpenZeppelin contract dependencies have been upgraded to version 5.4.0 across package.json and pnpm-lock.yaml.
  • Comprehensive Testing: A new dedicated test file, test/EXA.t.sol, has been added to thoroughly validate the ERC-7802 implementation, access control mechanisms, and core mint/burn functionalities.
  • Gas Snapshot Updates: Gas consumption values in .gas-snapshot have been updated to reflect the changes introduced by the new features and contract logic.
Changelog
  • .gas-snapshot
    • Updated gas consumption values for numerous tests.
  • contracts/periphery/EXA.sol
    • Implemented ERC-7802 interface.
    • Integrated AccessControl for managing bridge roles.
    • Introduced initializeV2 for admin role setup.
    • Added mint, burn, crosschainMint, and crosschainBurn functions with event emissions.
    • Updated supportsInterface to reflect new interfaces.
  • package.json
    • Updated OpenZeppelin contract dependencies to version 5.4.0.
  • pnpm-lock.yaml
    • Updated OpenZeppelin contract dependencies to version 5.4.0.
  • test/EXA.t.sol
    • Added new test suite for the EXA contract.
    • Covered initialization, role management, mint/burn operations, and interface support.
Activity
  • itofarina created this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

Adds BRIDGE_ROLE and IERC7802 cross-chain mint/burn to EXA with initialize2 reinitializer for proxy admin, updates tests to use proxy-based EXA and adds EXATest, bumps OpenZeppelin and CI/Foundry tooling, adjusts deploy scripts to handle timelock/ProxyAdmin, and updates many test gas snapshots.

Changes

Cohort / File(s) Summary
Core Contract
contracts/periphery/EXA.sol
Add BRIDGE_ROLE, implement IERC7802, inherit AccessControlUpgradeable, disable initializers in constructor, add initialize2(address), mint/burn wrappers, crosschainMint/crosschainBurn guarded by BRIDGE_ROLE, new events/errors, and update supportsInterface.
New Forge Tests
test/EXA.t.sol
Add EXATest suite deploying EXA behind ERC1967Proxy/ProxyAdmin, testing initialize/initialize2, proxy admin edge cases, BRIDGE_ROLE behavior, mint/burn and cross-chain flows, events, and interface support checks.
Test Updates / Gas Snapshot
.gas-snapshot, test/EscrowedEXA.t.sol
Massive gas-snapshot updates and test renames/migrations; change EscrowedEXA setup to proxy-based initialization and adjust chainId handling.
Deployment Changes
deploy/EXA.ts, deploy/Markets.ts
deploy/EXA.ts imports ProxyAdmin, adds onUpgrade calling initialize2(timelock), and proposes timelock-driven upgradeAndCall if timelock lacks admin; Markets.ts adds delegatecall to unsafeAllow.
Package & Tooling
package.json, foundry.toml, .github/workflows/*
Bump @openzeppelin packages to ^5.4.0, update Node and Foundry versions in CI, add coverage flag to script, increase Foundry gas_limit and remove deny_warnings, and add forge build/coverage steps in CI.
Hardhat Tests
test/hardhat/21_exa.ts
Add Hardhat test asserting timelock holds DEFAULT_ADMIN_ROLE on deployed EXA and deployer does not.
Changesets / Metadata
.changeset/*.md
Add patch changesets noting cross-chain mint/burn support and initialization restrictions.
Misc
.gitignore
Add .claude/settings.local.json to ignore list.

Sequence Diagram(s)

mermaid
sequenceDiagram
rect rgba(220,220,255,0.5)
participant Admin
end
rect rgba(200,255,200,0.5)
participant Proxy as ERC1967Proxy
end
rect rgba(255,240,200,0.5)
participant EXA
end
rect rgba(255,200,200,0.5)
participant Bridge
end

Admin->>Proxy: deploy proxy pointing to EXA impl
Admin->>Proxy: call initialize(...)
Admin->>Proxy: call initialize2(admin_)
Proxy->>EXA: delegatecall initialize / initialize2
Admin->>EXA: grantRole(BRIDGE_ROLE, Bridge)
Bridge->>EXA: crosschainMint(to, amount)
EXA-->>Bridge: emit CrosschainMint; update balances
Bridge->>EXA: crosschainBurn(from, amount)
EXA-->>Bridge: emit CrosschainBurn; update balances

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: adding cross-chain mint and burn support to the EXA contract, which is the primary objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch exa-crosschain

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

gemini-code-assist[bot]

This comment was marked as resolved.

@openzeppelin-code
Copy link

openzeppelin-code bot commented Feb 25, 2026

✨ exa: add crosschain mint and burn support

Generated at commit: e02a9739539feb283d1e468b50ff02c60b9dd894

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
2
4
0
7
26
39
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

coderabbitai[bot]

This comment was marked as resolved.

@itofarina itofarina force-pushed the exa-crosschain branch 2 times, most recently from 5edfce9 to df5529c Compare February 25, 2026 15:46
@itofarina itofarina changed the title ✨ exa: add ERC-7802 support ✨ exa: add crosschain mint and burn support Feb 25, 2026
coderabbitai[bot]

This comment was marked as resolved.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
contracts/periphery/EXA.sol (1)

26-29: ⚠️ Potential issue | 🟠 Major

Validate admin_ in initialize2 to prevent irreversible admin lockout.

Line 28 grants DEFAULT_ADMIN_ROLE without validating admin_. Passing address(0) would leave admin control effectively unusable.

🛠️ Proposed fix
 contract EXA is ERC20VotesUpgradeable, AccessControlUpgradeable, IERC7802 {
   bytes32 public constant BRIDGE_ROLE = keccak256("BRIDGE_ROLE");
+  error InvalidAdmin();
@@
   function initialize2(address admin_) external reinitializer(2) {
+    if (admin_ == address(0)) revert InvalidAdmin();
     __AccessControl_init();
     _grantRole(DEFAULT_ADMIN_ROLE, admin_);
   }
#!/bin/bash
set -euo pipefail

echo "== Verify EXA.initialize2 has/hasn't zero-address guard =="
rg -n -C3 'function initialize2|admin_ == address\(0\)|_grantRole\(DEFAULT_ADMIN_ROLE, admin_\)' contracts/periphery/EXA.sol

echo
echo "== Inspect OZ AccessControlUpgradeable _grantRole behavior =="
fd -HI '^AccessControlUpgradeable\.sol$' | while read -r f; do
  echo "-- $f"
  rg -n -C3 'function _grantRole|address\(0\)' "$f"
done

Expected result: no admin_ == address(0) guard in initialize2, and _grantRole itself does not enforce non-zero accounts.


ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df5529c and be4bd71.

📒 Files selected for processing (5)
  • .changeset/silent-chefs-marry.md
  • .gas-snapshot
  • contracts/periphery/EXA.sol
  • test/EXA.t.sol
  • test/EscrowedEXA.t.sol

@itofarina itofarina force-pushed the exa-crosschain branch 2 times, most recently from 0c63b60 to ebde601 Compare February 26, 2026 16:40
coderabbitai[bot]

This comment was marked as resolved.

@itofarina itofarina force-pushed the exa-crosschain branch 3 times, most recently from 87bb3d8 to 62e1258 Compare March 5, 2026 13:40
@itofarina itofarina marked this pull request as ready for review March 5, 2026 15:45
@itofarina itofarina requested a review from cruzdanilo as a code owner March 5, 2026 15:45
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

devin-ai-integration[bot]

This comment was marked as resolved.

@sentry
Copy link

sentry bot commented Mar 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@c62bf4c). Learn more about missing BASE report.
⚠️ Report is 134 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #797   +/-   ##
=======================================
  Coverage        ?   94.82%           
=======================================
  Files           ?       31           
  Lines           ?     2724           
  Branches        ?      457           
=======================================
  Hits            ?     2583           
  Misses          ?      140           
  Partials        ?        1           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@itofarina itofarina changed the title ✨ exa: add crosschain mint and burn support ✨ exa: add cross-chain mint and burn support Mar 10, 2026
@itofarina itofarina changed the title ✨ exa: add cross-chain mint and burn support ✨ exa: add crosschain mint and burn support Mar 10, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a3c5f93a3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


const exa = await getContract<EXA>("EXA", await getSigner(deployer));

if (!(await exa.hasRole(await exa.DEFAULT_ADMIN_ROLE(), timelock))) {

Choose a reason for hiding this comment

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

P1 Badge Avoid probing AccessControl before EXA upgrade executes

This hasRole check assumes the proxy is already running the new EXA implementation, but in the timelock/multisig flow validateUpgrade often only schedules/proposes the upgrade and returns before execution; in that state the proxy still points to the old EXA, which does not implement hasRole/DEFAULT_ADMIN_ROLE, so this call reverts and aborts the deploy script. This can break the first rollout of this commit on live networks unless the upgrade is executed manually before rerunning.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a24e1be-a859-4711-87b5-f3db75d9febb

📥 Commits

Reviewing files that changed from the base of the PR and between ebde601 and 8a3c5f9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • .changeset/silent-chefs-marry.md
  • .changeset/slick-feet-jump.md
  • .gas-snapshot
  • .github/workflows/release.yaml
  • .github/workflows/test.yml
  • .gitignore
  • contracts/periphery/EXA.sol
  • deploy/EXA.ts
  • deploy/Markets.ts
  • foundry.toml
  • package.json
  • test/EXA.t.sol
  • test/EscrowedEXA.t.sol
  • test/hardhat/21_exa.ts

Comment on lines 21 to +27
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: pnpm
- uses: foundry-rs/foundry-toolchain@v1
with:
version: v1.3.6
version: v1.5.1
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

What is the latest version of Foundry toolchain?

💡 Result:

The latest Foundry toolchain release is Foundry v1.6.0 (released January 22, 2026). [1]

A pre-release is also listed: v1.6.0-rc1. [1]

Source(s)
[1] (getfoundry.sh)

Citations:


LGTM - Version upgrades look appropriate, though a newer version exists.

Node.js 22 is the current LTS version. The Foundry toolchain upgrade to v1.5.1 is a solid improvement. However, Foundry v1.6.0 (released January 2026) is now the latest stable version and could be considered for future updates if you want to stay on the cutting edge.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: efd6c12d-5a40-4c5a-8a6e-ee9fb71ee4a5

📥 Commits

Reviewing files that changed from the base of the PR and between 8a3c5f9 and e02a973.

📒 Files selected for processing (8)
  • .changeset/silent-chefs-marry.md
  • .changeset/slick-feet-jump.md
  • .gas-snapshot
  • contracts/periphery/EXA.sol
  • deploy/EXA.ts
  • test/EXA.t.sol
  • test/EscrowedEXA.t.sol
  • test/hardhat/21_exa.ts

Comment on lines +49 to +56
if (!(await exa.hasRole(await exa.DEFAULT_ADMIN_ROLE(), timelock))) {
const proxyAdmin = await getContract<ProxyAdmin>("ProxyAdmin", await getSigner(deployer));
await timelockPropose(proxyAdmin, "upgradeAndCall", [
exa.target,
(await get("EXA_Implementation")).address,
exa.interface.encodeFunctionData("initialize2", [timelock]),
]);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for any BRIDGE_ROLE grants in deployment or scripts
rg -n 'BRIDGE_ROLE|grantRole.*bridge' --type ts --type js -C2

Repository: exactly/protocol

Length of output: 42


🏁 Script executed:

# First, find the EXA contract file
find . -type f -name "*EXA*" | grep -E "\.(sol|ts|js)$" | head -20

Repository: exactly/protocol

Length of output: 257


🏁 Script executed:

# Search for mint and burn function definitions
rg -n "function mint|function burn" --type ts --type js --type sol -A3

Repository: exactly/protocol

Length of output: 87


🏁 Script executed:

# Search for role definitions (including different naming patterns)
rg -n "ROLE|keccak256.*role|bytes32.*=" --type ts --type js --type sol | grep -i "admin\|bridge\|role" | head -30

Repository: exactly/protocol

Length of output: 87


🏁 Script executed:

# Read the EXA.sol file to understand role structure
cat ./contracts/periphery/EXA.sol | head -100

Repository: exactly/protocol

Length of output: 2845


🏁 Script executed:

# Search for BRIDGE_ROLE, mint, burn in EXA.sol
grep -n "BRIDGE_ROLE\|function mint\|function burn\|bytes32.*ROLE\|initialize2" ./contracts/periphery/EXA.sol

Repository: exactly/protocol

Length of output: 494


🏁 Script executed:

# Check the deploy/EXA.ts file structure
cat ./deploy/EXA.ts

Repository: exactly/protocol

Length of output: 4163


Add BRIDGE_ROLE grant to timelock during EXA deployment or confirm it's granted via a separate governance proposal.

The mint() and burn() functions require BRIDGE_ROLE to be callable (they delegate to crosschainMint and crosschainBurn which are guarded by onlyRole(BRIDGE_ROLE)), but the deployment script only grants DEFAULT_ADMIN_ROLE to timelock via initialize2. Without BRIDGE_ROLE being granted—either in this deployment or via a subsequent governance proposal—the mint/burn functions remain non-functional. Ensure the role grant path is explicitly documented and implemented.

Comment on lines +158 to +161
function test_mint_reverts_whenCallerLacksBridgeRole() external {
vm.expectRevert();
exa.mint(bob, 100e18);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider asserting specific revert selectors in negative-path tests.

Using bare vm.expectRevert() can mask unrelated failures. For more robust regression tests, assert the expected AccessControlUnauthorizedAccount selector.

Example for one test
 function test_mint_reverts_whenCallerLacksBridgeRole() external {
-    vm.expectRevert();
+    vm.expectRevert(
+      abi.encodeWithSelector(
+        IAccessControl.AccessControlUnauthorizedAccount.selector,
+        address(this),
+        exa.BRIDGE_ROLE()
+      )
+    );
     exa.mint(bob, 100e18);
 }

Also applies to: 174-177, 189-192, 205-208

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