Skip to content

Conversation

@pegahcarter
Copy link
Collaborator

@pegahcarter pegahcarter commented Aug 4, 2025

This PR merges:

into one single audit-able branch. The goal here is to separate the concerns across PRs and then combine when each is deemed sufficient. In addition, this PR provides the mainnet upgrade scripts used, which also require an audit.

* feat: FraxOFTMintableAdapterUpgradeable with script

* fix: recover()

* feat: build comptroller addMinter txs

* wip: test

* test: passing send, receive for (s)frxUSD, fpi

* refactor: label fraxtal script, rm wfrax

* feat: init 1b

* chore: rm 1b for later

* feat: track supply

* feedback

* feat: initialTotalSupply

* fix: reset totalTransferX when setting initialSupply

* refactor: upgrade to v110 dir
* feat: frxUsd compliance

* fix: _disableInitializers()

* refactor: optimize for verkle

* chore: init v1.1.0 scripts

* refactor: mv modules to dir

* refactor: frozen array, error

* test: FrxUSDOFTUpgradeable coverage

* refactor: always passing freeze/thaw
* feat: EIP3009Module, PermitModule

* build: update

* build: pnpm up

* feat: support eip 712 in OFT

* test: permit works

* refactor: public cancelAuthorization()

* test: add hashing for authorization

* fix: compile

* test: authorization coverage

* feat: view of nonce use

* test: coverage

* feat: eip2619, 3009 to all OFTs

* refactor: SignatureChecker in 712, 2612 supports 1271

* refactor: SignatureModule

* chore: build

* feat: format file
@pegahcarter pegahcarter marked this pull request as ready for review August 12, 2025 20:58
@pegahcarter pegahcarter changed the title [WIP] OFT v1.1.0 upgrade OFT v1.1.0 upgrade Aug 12, 2025
Copy link

@tom2o17 tom2o17 left a comment

Choose a reason for hiding this comment

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

LGTM

Couple of nit comments.

I also ran into a VM issue with snapshotState() when

rm -rf node_modules
pnpm i 
forge build 

but that might be a me issue

major = 1;
minor = 0;
patch = 1;
function version() public pure returns (string memory) {
Copy link

Choose a reason for hiding this comment

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

Nit: Personally prefer the old convention but defer to you

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I converted this to a string so that signatures can utilize the version of the contract:

__EIP712_init(_name, version());

One thing to note is that future versions will cause prior signatures to be invalid.

}

/// @dev This method is called specifically when upgrading an existing OFT
function initializeV110() external reinitializer(3) {
Copy link

Choose a reason for hiding this comment

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

Won't only either initialize() or initializeV110 be callable ?

Would it not make more sense to just remove the one that is not intended to be used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's correct, either only initialize() or initializeV110() is able to be called. Having both within the contract allows one contract where:

  • It can solely upgrade existing OFTs to EIP-712 compatible (as it is in this upgrade)
  • It can be deployed on a fresh chain while maintaining the deploy script

Ultimately, we have one implementation that is used for both cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tom2o17 just reviewed this and realized only the dual initialize() functions are needed for frxUSD as WFRAX and sfrxUSD deployments are using FraxOFTUpgradeable.sol. Good catch here, I removed the extra methods in eee52da.

contract WFRAXTokenOFTUpgradeable is OFTUpgradeable {
import {EIP3009Module} from "contracts/modules/EIP3009Module.sol";
import {PermitModule} from "contracts/modules/PermitModule.sol";

Copy link

Choose a reason for hiding this comment

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

NIT: - Q: do we want the freeze thaw module here too ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question! I'll raise on tg.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Team confirmed, only freeze/thaw for frxUSD.

/// @param accounts Array of accounts to be frozen
/// @dev Added in v1.1.0
function freezeMany(address[] memory accounts) external {
if (!isFreezer(msg.sender)) revert NotFreezer();
Copy link

Choose a reason for hiding this comment

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

Nit: do we want to allow owner to bypass. Could go either way, would not be difficult to batch if role is not granted

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will add.


function initialize(string memory _name, string memory _symbol, address _delegate) external initializer {
/// @dev This method is called specifically when deploying a new OFT
function initialize(string memory _name, string memory _symbol, address _delegate) external reinitializer(3) {
Copy link

Choose a reason for hiding this comment

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

Same here

/// @title Eip3009
/// @notice Eip3009 provides internal implementations for gas-abstracted transfers under Eip3009 guidelines
/// @author Frax Finance, inspired by Agora (thanks Drake)
abstract contract EIP3009Module is SignatureModule {
Copy link

Choose a reason for hiding this comment

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

Nit: Potentially add

/**
 * @notice Returns the state of an authorization
 * @dev Nonces are randomly generated 32-byte data unique to the authorizer's
 * address
 * @param authorizer    Authorizer's address
 * @param nonce         Nonce of the authorization
 * @return True if the nonce is used
 */
function authorizationState(
    address authorizer,
    bytes32 nonce
) external view returns (bool) {
   return _getEIP3009ModuleStorage().isAuthorizationUsed[authorizer][nonce];
}
Screenshot 2025-08-15 at 2 54 14 PM

Just to check all of the boxes for EIP-3009 as defined in
https://eips.ethereum.org/EIPS/eip-3009

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice catch, will add.

* @title SupplyTrackingModule
* @notice Contract module to track circulating supply of an OFT across all destination chains
*/
abstract contract SupplyTrackingModule {
Copy link

Choose a reason for hiding this comment

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

Nit: potoentially make an event

@pegahcarter
Copy link
Collaborator Author

I also ran into a VM issue with snapshotState() when
...
but that might be a me issue

I reproduced your issue after following the same commands. Even after syncing packages to their latest version I still had the same issue. Will debug after addressing the other comments.

@dhruvinparikh
Copy link
Contributor

forge build fails with following error:

Error: Compiler run failed:
Error (7364): Different number of components on the left hand side (3) than on the right hand side (1).
  --> scripts/ops/testnet/UpgradeEthSepoliaOFT/2_UpgradeOFT.s.sol:61:9:
   |
61 |         (uint256 major, uint256 minor, uint256 patch) = FraxOFTAdapterUpgradeable(oft).version();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Error (9574): Type string memory is not implicitly convertible to expected type uint256.
  --> scripts/ops/testnet/UpgradeEthSepoliaOFT/2_UpgradeOFT.s.sol:61:9:
   |
61 |         (uint256 major, uint256 minor, uint256 patch) = FraxOFTAdapterUpgradeable(oft).version();

@pegahcarter
Copy link
Collaborator Author

@dhruvinparikh it'll compile now. I was working with a modified foundry.toml to speed up testing by only compiling contracts/.

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