Commit 196613d9 authored by Disco's avatar Disco Committed by GitHub

feat: support permit2 on superchainweth (#12596)

* feat: support permit2 on superchainweth

* chore: run pre-pr

---------
Co-authored-by: default avataragusduha <agusnduha@gmail.com>
Co-authored-by: default avatargotzenx <78360669+gotzenx@users.noreply.github.com>
parent c23daa00
...@@ -132,12 +132,12 @@ ...@@ -132,12 +132,12 @@
"sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036"
}, },
"src/L2/SuperchainWETH.sol": { "src/L2/SuperchainWETH.sol": {
"initCodeHash": "0xc120d1fd9edd8aa392cf112859a3a3907c3b5d55132d6a5edd80ff1d6b6baeaa", "initCodeHash": "0xc72cb486b815a65daa8bd5d0af8c965b6708cf8caf03de0a18023a63a6e6c604",
"sourceCodeHash": "0x445f088aa91fafde43f558f34da4b9f85b82e6a76f8b2dec9563838eb4335928" "sourceCodeHash": "0x39fff1d4702a2fec3dcba29c7f9604eabf20d32e9c5bf4377edeb620624aa467"
}, },
"src/L2/WETH.sol": { "src/L2/WETH.sol": {
"initCodeHash": "0xfb253765520690623f177941c2cd9eba23e4c6d15063bccdd5e98081329d8956", "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17",
"sourceCodeHash": "0x2ab6be69795109a1ee04c5693a34d6ce0ff90b62e404cdeb18178bab18d06784" "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b"
}, },
"src/cannon/MIPS.sol": { "src/cannon/MIPS.sol": {
"initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2", "initCodeHash": "0x696c3ce334c11d0f9633945babac22b1b65848ff00d2cf6c4cb18116bbf138b2",
...@@ -156,8 +156,8 @@ ...@@ -156,8 +156,8 @@
"sourceCodeHash": "0x1d918a536d9f6c900efdf069e96c2a27bb49340d6d1ebaa92dd6b481835a9a82" "sourceCodeHash": "0x1d918a536d9f6c900efdf069e96c2a27bb49340d6d1ebaa92dd6b481835a9a82"
}, },
"src/dispute/DelayedWETH.sol": { "src/dispute/DelayedWETH.sol": {
"initCodeHash": "0x835b322de7d5c84b415e99f2cb1000411df18995b5476f2116ac6f897f2d0910", "initCodeHash": "0xb31e0ff80fd69bc3f3b7d53f3fa42da4cdae393e41b8816719ce5ebe3d248688",
"sourceCodeHash": "0xdbd64724b73f8f9d6f1cc72bb662a99b9955ab72950a8f6ffeb1d2454997f60b" "sourceCodeHash": "0x1dfc68560c0805faa78360e3d4ef2d768e2f3d6c0c7183d2077a2c4277c778db"
}, },
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0xd72eced7cb5400d93188038a707fe6c1b04077f059cd8e2f5253e871de2cee3b", "initCodeHash": "0xd72eced7cb5400d93188038a707fe6c1b04077f059cd8e2f5253e871de2cee3b",
......
...@@ -22,12 +22,12 @@ ...@@ -22,12 +22,12 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "owner",
"type": "address" "type": "address"
}, },
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "spender",
"type": "address" "type": "address"
} }
], ],
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "src",
"type": "address" "type": "address"
} }
], ],
......
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "owner",
"type": "address" "type": "address"
}, },
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "spender",
"type": "address" "type": "address"
} }
], ],
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "src",
"type": "address" "type": "address"
} }
], ],
......
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "owner",
"type": "address" "type": "address"
}, },
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "spender",
"type": "address" "type": "address"
} }
], ],
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "src",
"type": "address" "type": "address"
} }
], ],
......
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "owner",
"type": "address" "type": "address"
}, },
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "spender",
"type": "address" "type": "address"
} }
], ],
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "src",
"type": "address" "type": "address"
} }
], ],
......
...@@ -36,14 +36,14 @@ ...@@ -36,14 +36,14 @@
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "balanceOf", "label": "_balanceOf",
"offset": 0, "offset": 0,
"slot": "101", "slot": "101",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "allowance", "label": "_allowance",
"offset": 0, "offset": 0,
"slot": "102", "slot": "102",
"type": "mapping(address => mapping(address => uint256))" "type": "mapping(address => mapping(address => uint256))"
......
[ [
{ {
"bytes": "32", "bytes": "32",
"label": "balanceOf", "label": "_balanceOf",
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "allowance", "label": "_allowance",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(address => mapping(address => uint256))" "type": "mapping(address => mapping(address => uint256))"
......
[ [
{ {
"bytes": "32", "bytes": "32",
"label": "balanceOf", "label": "_balanceOf",
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "allowance", "label": "_allowance",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(address => mapping(address => uint256))" "type": "mapping(address => mapping(address => uint256))"
......
[ [
{ {
"bytes": "32", "bytes": "32",
"label": "balanceOf", "label": "_balanceOf",
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "allowance", "label": "_allowance",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
"type": "mapping(address => mapping(address => uint256))" "type": "mapping(address => mapping(address => uint256))"
......
...@@ -6,6 +6,7 @@ import { WETH98 } from "src/universal/WETH98.sol"; ...@@ -6,6 +6,7 @@ import { WETH98 } from "src/universal/WETH98.sol";
// Libraries // Libraries
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
// Interfaces // Interfaces
import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol";
...@@ -22,8 +23,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro ...@@ -22,8 +23,8 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro
/// do not use a custom gas token. /// do not use a custom gas token.
contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.0.0-beta.8 /// @custom:semver 1.0.0-beta.9
string public constant version = "1.0.0-beta.8"; string public constant version = "1.0.0-beta.9";
/// @inheritdoc WETH98 /// @inheritdoc WETH98
function deposit() public payable override { function deposit() public payable override {
...@@ -37,11 +38,17 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { ...@@ -37,11 +38,17 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver {
super.withdraw(_amount); super.withdraw(_amount);
} }
/// @inheritdoc WETH98
function allowance(address owner, address spender) public view override returns (uint256) {
if (spender == Preinstalls.Permit2) return type(uint256).max;
return super.allowance(owner, spender);
}
/// @notice Mints WETH to an address. /// @notice Mints WETH to an address.
/// @param _to The address to mint WETH to. /// @param _to The address to mint WETH to.
/// @param _amount The amount of WETH to mint. /// @param _amount The amount of WETH to mint.
function _mint(address _to, uint256 _amount) internal { function _mint(address _to, uint256 _amount) internal {
balanceOf[_to] += _amount; _balanceOf[_to] += _amount;
emit Transfer(address(0), _to, _amount); emit Transfer(address(0), _to, _amount);
} }
...@@ -49,7 +56,7 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver { ...@@ -49,7 +56,7 @@ contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver {
/// @param _from The address to burn WETH from. /// @param _from The address to burn WETH from.
/// @param _amount The amount of WETH to burn. /// @param _amount The amount of WETH to burn.
function _burn(address _from, uint256 _amount) internal { function _burn(address _from, uint256 _amount) internal {
balanceOf[_from] -= _amount; _balanceOf[_from] -= _amount;
emit Transfer(_from, address(0), _amount); emit Transfer(_from, address(0), _amount);
} }
......
...@@ -14,8 +14,8 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; ...@@ -14,8 +14,8 @@ import { IL1Block } from "src/L2/interfaces/IL1Block.sol";
/// @title WETH contract that reads the name and symbol from the L1Block contract. /// @title WETH contract that reads the name and symbol from the L1Block contract.
/// Allows for nice rendering of token names for chains using custom gas token. /// Allows for nice rendering of token names for chains using custom gas token.
contract WETH is WETH98, ISemver { contract WETH is WETH98, ISemver {
/// @custom:semver 1.1.0-beta.2 /// @custom:semver 1.1.0-beta.3
string public constant version = "1.1.0-beta.2"; string public constant version = "1.1.0-beta.3";
/// @notice Returns the name of the wrapped native asset. Will be "Wrapped Ether" /// @notice Returns the name of the wrapped native asset. Will be "Wrapped Ether"
/// if the native asset is Ether. /// if the native asset is Ether.
......
...@@ -32,8 +32,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { ...@@ -32,8 +32,8 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
event Unwrap(address indexed src, uint256 wad); event Unwrap(address indexed src, uint256 wad);
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.2.0-beta.2 /// @custom:semver 1.2.0-beta.3
string public constant version = "1.2.0-beta.2"; string public constant version = "1.2.0-beta.3";
/// @notice Returns a withdrawal request for the given address. /// @notice Returns a withdrawal request for the given address.
mapping(address => mapping(address => WithdrawalRequest)) public withdrawals; mapping(address => mapping(address => WithdrawalRequest)) public withdrawals;
...@@ -112,7 +112,7 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { ...@@ -112,7 +112,7 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
/// @param _wad The amount of WETH to recover. /// @param _wad The amount of WETH to recover.
function hold(address _guy, uint256 _wad) external { function hold(address _guy, uint256 _wad) external {
require(msg.sender == owner(), "DelayedWETH: not owner"); require(msg.sender == owner(), "DelayedWETH: not owner");
allowance[_guy][msg.sender] = _wad; _allowance[_guy][msg.sender] = _wad;
emit Approval(_guy, msg.sender, _wad); emit Approval(_guy, msg.sender, _wad);
} }
} }
...@@ -26,8 +26,8 @@ import { IWETH } from "src/universal/interfaces/IWETH.sol"; ...@@ -26,8 +26,8 @@ import { IWETH } from "src/universal/interfaces/IWETH.sol";
contract WETH98 is IWETH { contract WETH98 is IWETH {
uint8 public constant decimals = 18; uint8 public constant decimals = 18;
mapping(address => uint256) public balanceOf; mapping(address => uint256) internal _balanceOf;
mapping(address => mapping(address => uint256)) public allowance; mapping(address => mapping(address => uint256)) internal _allowance;
/// @notice Pipes to deposit. /// @notice Pipes to deposit.
receive() external payable { receive() external payable {
...@@ -49,16 +49,26 @@ contract WETH98 is IWETH { ...@@ -49,16 +49,26 @@ contract WETH98 is IWETH {
return "WETH"; return "WETH";
} }
/// @inheritdoc IWETH
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowance[owner][spender];
}
/// @inheritdoc IWETH
function balanceOf(address src) public view returns (uint256) {
return _balanceOf[src];
}
/// @inheritdoc IWETH /// @inheritdoc IWETH
function deposit() public payable virtual { function deposit() public payable virtual {
balanceOf[msg.sender] += msg.value; _balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value); emit Deposit(msg.sender, msg.value);
} }
/// @inheritdoc IWETH /// @inheritdoc IWETH
function withdraw(uint256 wad) public virtual { function withdraw(uint256 wad) public virtual {
require(balanceOf[msg.sender] >= wad); require(_balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad; _balanceOf[msg.sender] -= wad;
payable(msg.sender).transfer(wad); payable(msg.sender).transfer(wad);
emit Withdrawal(msg.sender, wad); emit Withdrawal(msg.sender, wad);
} }
...@@ -70,7 +80,7 @@ contract WETH98 is IWETH { ...@@ -70,7 +80,7 @@ contract WETH98 is IWETH {
/// @inheritdoc IWETH /// @inheritdoc IWETH
function approve(address guy, uint256 wad) external returns (bool) { function approve(address guy, uint256 wad) external returns (bool) {
allowance[msg.sender][guy] = wad; _allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad); emit Approval(msg.sender, guy, wad);
return true; return true;
} }
...@@ -82,15 +92,16 @@ contract WETH98 is IWETH { ...@@ -82,15 +92,16 @@ contract WETH98 is IWETH {
/// @inheritdoc IWETH /// @inheritdoc IWETH
function transferFrom(address src, address dst, uint256 wad) public returns (bool) { function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
require(balanceOf[src] >= wad); require(_balanceOf[src] >= wad);
if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { uint256 senderAllowance = allowance(src, msg.sender);
require(allowance[src][msg.sender] >= wad); if (src != msg.sender && senderAllowance != type(uint256).max) {
allowance[src][msg.sender] -= wad; require(senderAllowance >= wad);
_allowance[src][msg.sender] -= wad;
} }
balanceOf[src] -= wad; _balanceOf[src] -= wad;
balanceOf[dst] += wad; _balanceOf[dst] += wad;
emit Transfer(src, dst, wad); emit Transfer(src, dst, wad);
......
...@@ -7,6 +7,7 @@ import { CommonTest } from "test/setup/CommonTest.sol"; ...@@ -7,6 +7,7 @@ import { CommonTest } from "test/setup/CommonTest.sol";
// Libraries // Libraries
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; import { NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
// Interfaces // Interfaces
import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol";
...@@ -373,4 +374,83 @@ contract SuperchainWETH_Test is CommonTest { ...@@ -373,4 +374,83 @@ contract SuperchainWETH_Test is CommonTest {
// Assert // Assert
assertFalse(success); assertFalse(success);
} }
/// @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 _src The funds owner.
function testFuzz_allowance_fromPermit2_succeeds(address _randomCaller, address _src) public {
vm.prank(_randomCaller);
uint256 _allowance = superchainWeth.allowance(_src, 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 _src The funds owner.
/// @param _guy The address of the spender - It cannot be Permit2.
function testFuzz_allowance_succeeds(address _randomCaller, address _src, address _guy, uint256 _wad) public {
// Assume
vm.assume(_guy != Preinstalls.Permit2);
// Arrange
vm.prank(_src);
superchainWeth.approve(_guy, _wad);
// Act
vm.prank(_randomCaller);
uint256 _allowance = superchainWeth.allowance(_src, _guy);
// Assert
assertEq(_allowance, _wad);
}
/// @notice Tests that `transferFrom` works when the caller (spender) is Permit2, without any explicit approval.
/// @param _src The funds owner.
/// @param _dst The address of the recipient.
/// @param _wad The amount of WETH to transfer.
function testFuzz_transferFrom_whenPermit2IsCaller_succeeds(address _src, address _dst, uint256 _wad) public {
vm.assume(_src != _dst);
// Arrange
deal(address(superchainWeth), _src, _wad);
vm.expectEmit(address(superchainWeth));
emit Transfer(_src, _dst, _wad);
// Act
vm.prank(Preinstalls.Permit2);
superchainWeth.transferFrom(_src, _dst, _wad);
// Assert
assertEq(superchainWeth.balanceOf(_src), 0);
assertEq(superchainWeth.balanceOf(_dst), _wad);
}
/// @notice Tests that `transferFrom` works when the caller (spender) is Permit2, and `_src` equals `_dst` without
/// an explicit approval.
/// The balance should remain the same on this scenario.
/// @param _user The source and destination address.
/// @param _wad The amount of WETH to transfer.
function testFuzz_transferFrom_whenPermit2IsCallerAndSourceIsDestination_succeeds(
address _user,
uint256 _wad
)
public
{
// Arrange
deal(address(superchainWeth), _user, _wad);
vm.expectEmit(address(superchainWeth));
emit Transfer(_user, _user, _wad);
// Act
vm.prank(Preinstalls.Permit2);
superchainWeth.transferFrom(_user, _user, _wad);
// Assert
assertEq(superchainWeth.balanceOf(_user), _wad);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment