Skip to content

Commit

Permalink
feat: support permit2 on OptimismSuperchainERC20 + upgrade solady's e…
Browse files Browse the repository at this point in the history
…rc20 implementation (#12476)

* feat: add superchain erc20 bridge (#61)

* feat: add superchain erc20 bridge

* fix: interfaces and versions

* refactor: optimism superchain erc20 redesign (#62)

* refactor: use oz upgradeable erc20 as dependency

* chore: update interfaces

* fix: tests based on changes

* refactor: remove op as dependency

* feat: add check for supererc20 bridge on modifier

* chore: update tests and interfaces

* chore: update stack vars name on test

* chore: remove empty gitmodules file

* chore: update superchain weth errors

* test: add superchain erc20 bridge tests (#65)

* test: add superchain erc20 bridge tests

* test: add optimism superchain erc20 beacon tests

* test: remove unnecessary test

* test: tests fixes

* test: tests fixes

* chore: update missing bridge on natspec (#69)

* chore: update missing bridge on natspec

* fix: natspecs

---------

Co-authored-by: agusduha <[email protected]>

* fix: remove superchain erc20 base (#70)

* refactor: update isuperchainweth (#71)


---------

Co-authored-by: agusduha <[email protected]>

* feat: rename mint/burn and add SuperchainERC20 (#74)

* refactor: rename mint and burn functions on superchain erc20

* chore: rename optimism superchain erc20 to superchain erc20

* feat: create optimism superchain erc20 contract

* chore: update natspec and errors

* fix: superchain erc20 tests

* refactor: make superchain erc20 abstract

* refactor: move storage and erc20 metadata functions to implementation

* chore: update interfaces

* chore: update superchain erc20 events

* fix: tests

* fix: natspecs

* fix: add semmver lock and snapshots

* fix: remove unused imports

* fix: natspecs

---------

Co-authored-by: 0xDiscotech <[email protected]>

* fix: refactor zero check (#76)

* fix: pre pr

* chore: add new solady version and import it for erc20

* fix: undo forge std changes

* chore: re run pre pr script

* fix: semver natspec check failure (#79)

* fix: semver natspec check failure

* fix: ignore mock contracts in semver natspec script

* fix: error message

* feat: add crosschain erc20 interface (#80)

* feat: add crosschain erc20 interface

* fix: refactor interfaces

* fix: superchain bridge natspec (#83)

* fix: superchain weth natspec (#84)

Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>

* fix: stop inheriting superchain interfaces (#85)

* fix: stop inheriting superchain interfaces

* fix: move events and erros into the implementation

* fix: make superchainERC20 inherits from crosschainERC20

* fix: superchain bridge rename (#86)

* fix: fee vault compiler error (#87)

* fix: remove unused imports

* chore: run pre-pr and update vendor interface

* fix: refactor common errors (#90)

* fix: refactor common errors

* fix: remove unused version

* feat: add permit2 on optimism superchain erc20

* chore: run pre-pr script

* fix: reuse unauthorized error (#92)

* fix: superchain erc20 factory conflicts

* fix: rename crosschain functions (#94)

* chore: run pre-pr

* chore: run pre-pr

* chore: run pre-pr

* feat: add new tests on optimism superchain erc20

* fix: vars and params naming on newly added tests

* fix: var name

* feat: support permit2 on optimism superchain erc20 and upgrade solady's erc20 implementation (#97)


---
Co-Authored-by: AgusDuha <[email protected]>

* chore: use ierc20 alias for ierc20 solady interface (#108)

---------

Co-authored-by: AgusDuha <[email protected]>
Co-authored-by: agusduha <[email protected]>
Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>
  • Loading branch information
6 people authored Oct 22, 2024
1 parent 2d08d19 commit cd640b0
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@
[submodule "packages/contracts-bedrock/lib/openzeppelin-contracts-v5"]
path = packages/contracts-bedrock/lib/openzeppelin-contracts-v5
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "packages/contracts-bedrock/lib/solady-v0.0.245"]
path = packages/contracts-bedrock/lib/solady-v0.0.245
url = https://github.com/vectorized/solady
1 change: 1 addition & 0 deletions packages/contracts-bedrock/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ remappings = [
'@rari-capital/solmate/=lib/solmate',
'@lib-keccak/=lib/lib-keccak/contracts/lib',
'@solady/=lib/solady/src',
'@solady-v0.0.245/=lib/solady-v0.0.245/src',
'forge-std/=lib/forge-std/src',
'ds-test/=lib/forge-std/lib/ds-test/src',
'safe-contracts/=lib/safe-contracts/contracts',
Expand Down
1 change: 1 addition & 0 deletions packages/contracts-bedrock/lib/solady-v0.0.245
Submodule solady-v0.0.245 added at e0ef35
6 changes: 3 additions & 3 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@
"sourceCodeHash": "0x4bb08a8a9d5de37d1fb2dd8a9bcc483305510c32508f0be2d54757e6e257aedb"
},
"src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0xd5c84e45746fd741d541a917ddc1cc0c7043c6b21d5c18040d4bc999d6a7b2db",
"sourceCodeHash": "0xf32130f0b46333daba062c50ff6dcfadce1f177ff753bed2374d499ea9c2d98a"
"initCodeHash": "0x4e25579079d73c93f1d494e1976334b77fc4ec181c67f376d8e2613c7b207f52",
"sourceCodeHash": "0xe41cf3b005f1ea007fc1b5f69f630be5f6ef12d6e5e94a50e3160b0ebe0a1613"
},
"src/L2/OptimismSuperchainERC20Beacon.sol": {
"initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab",
Expand All @@ -125,7 +125,7 @@
},
"src/L2/SuperchainERC20.sol": {
"initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"sourceCodeHash": "0x75d061633a141af11a19b86e599a1725dfae8d245dcddfb6bb244a50d5e53f96"
"sourceCodeHash": "0x6a384ccfb6f2f7316c1b33873a1630b5179e52475951d31771656e06d2b11519"
},
"src/L2/SuperchainTokenBridge.sol": {
"initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,11 @@
"name": "NotInitializing",
"type": "error"
},
{
"inputs": [],
"name": "Permit2AllowanceIsFixedAtInfinity",
"type": "error"
},
{
"inputs": [],
"name": "PermitExpired",
Expand Down
9 changes: 7 additions & 2 deletions packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 {
}

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.6
string public constant override version = "1.0.0-beta.6";
/// @custom:semver 1.0.0-beta.7
string public constant override version = "1.0.0-beta.7";

/// @notice Constructs the OptimismSuperchainERC20 contract.
constructor() {
Expand Down Expand Up @@ -141,4 +141,9 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable, ERC165 {
function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
return _interfaceId == type(IOptimismSuperchainERC20).interfaceId || super.supportsInterface(_interfaceId);
}

/// @notice Sets Permit2 contract's allowance at infinity.
function _givePermit2InfiniteAllowance() internal view virtual override returns (bool) {
return true;
}
}
6 changes: 3 additions & 3 deletions packages/contracts-bedrock/src/L2/SuperchainERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.25;
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { ERC20 } from "@solady/tokens/ERC20.sol";
import { ERC20 } from "@solady-v0.0.245/tokens/ERC20.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";

/// @title SuperchainERC20
Expand All @@ -19,9 +19,9 @@ abstract contract SuperchainERC20 is ERC20, ICrosschainERC20, ISemver {
}

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.1
/// @custom:semver 1.0.0-beta.2
function version() external view virtual returns (string memory) {
return "1.0.0-beta.1";
return "1.0.0-beta.2";
}

/// @notice Allows the SuperchainTokenBridge to mint tokens.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ pragma solidity ^0.8.0;

// Interfaces
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol";
import { IERC20Solady } from "src/vendor/interfaces/IERC20Solady.sol";
import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";

/// @title ISuperchainERC20
/// @notice This interface is available on the SuperchainERC20 contract.
/// @dev This interface is needed for the abstract SuperchainERC20 implementation but is not part of the standard
interface ISuperchainERC20 is ICrosschainERC20, IERC20Solady, ISemver {
interface ISuperchainERC20 is ICrosschainERC20, IERC20, ISemver {
error Unauthorized();

function __constructor__() external;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ interface IERC20Solady {
/// @dev The permit has expired.
error PermitExpired();

/// @dev The allowance of Permit2 is fixed at infinity.
error Permit2AllowanceIsFixedAtInfinity();

/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);

Expand Down
64 changes: 63 additions & 1 deletion packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";

// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { IERC20 } from "@openzeppelin/contracts-v5/token/ERC20/IERC20.sol";
import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol";

import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol";
import { IERC165 } from "@openzeppelin/contracts-v5/utils/introspection/IERC165.sol";
import { IBeacon } from "@openzeppelin/contracts-v5/proxy/beacon/IBeacon.sol";
import { BeaconProxy } from "@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol";
import { Unauthorized } from "src/libraries/errors/CommonErrors.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";

// Target contract
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol";
Expand Down Expand Up @@ -252,4 +254,64 @@ contract OptimismSuperchainERC20Test is Test {
vm.assume(_interfaceId != type(IOptimismSuperchainERC20).interfaceId);
assertFalse(optimismSuperchainERC20.supportsInterface(_interfaceId));
}

/// @notice Tests that the allowance function returns the max uint256 value when the spender is Permit.
/// @param _randomCaller The address that will call the function - used to fuzz better since the behaviour should be
/// the same regardless of the caller.
/// @param _owner The funds owner.
function testFuzz_allowance_fromPermit2_succeeds(address _randomCaller, address _owner) public {
vm.prank(_randomCaller);
uint256 _allowance = optimismSuperchainERC20.allowance(_owner, Preinstalls.Permit2);

assertEq(_allowance, type(uint256).max);
}

/// @notice Tests that the allowance function returns the correct allowance when the spender is not Permit.
/// @param _randomCaller The address that will call the function - used to fuzz better
/// since the behaviour should be the same regardless of the caller.
/// @param _owner The funds owner.
/// @param _guy The address of the spender - It cannot be Permit2.
function testFuzz_allowance_succeeds(address _randomCaller, address _owner, address _guy, uint256 _amount) public {
// Assume
vm.assume(_guy != Preinstalls.Permit2);

// Arrange
vm.prank(_owner);
optimismSuperchainERC20.approve(_guy, _amount);

// Act
vm.prank(_randomCaller);
uint256 _allowance = optimismSuperchainERC20.allowance(_owner, _guy);

// Assert
assertEq(_allowance, _amount);
}

/// @notice Tests that `transferFrom` works when the caller (spender) is Permit2, without any explicit approval.
/// @param _owner The funds owner.
/// @param _recipient The address of the recipient.
/// @param _amount The amount of tokens to transfer.
function testFuzz_transferFrom_whenPermit2IsCaller_succeeds(
address _owner,
address _recipient,
uint256 _amount
)
public
{
// Arrange
deal(address(optimismSuperchainERC20), _owner, _amount);

vm.expectEmit(address(optimismSuperchainERC20));
emit IERC20.Transfer(_owner, _recipient, _amount);

// Act
vm.prank(Preinstalls.Permit2);
optimismSuperchainERC20.transferFrom(_owner, _recipient, _amount);

// Assert
assertEq(optimismSuperchainERC20.balanceOf(_recipient), _amount);
// Handle the case where the source and destination are the same to check the source balance.
if (_owner != _recipient) assertEq(optimismSuperchainERC20.balanceOf(_owner), 0);
else assertEq(optimismSuperchainERC20.balanceOf(_owner), _amount);
}
}
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol";

// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { IERC20 } from "@openzeppelin/contracts-v5/token/ERC20/IERC20.sol";
import { IERC20Solady as IERC20 } from "src/vendor/interfaces/IERC20Solady.sol";

// Target contract
import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol";
Expand Down

0 comments on commit cd640b0

Please sign in to comment.