Skip to content

Commit a2317fa

Browse files
committed
re-add the spender in the constructor
1 parent 1769f69 commit a2317fa

File tree

2 files changed

+33
-27
lines changed

2 files changed

+33
-27
lines changed

contracts/src/RemoteSuperchainERC20.sol

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ contract RemoteSuperchainERC20 is ERC20 {
1818
/// @notice the chain the ERC20 lives on
1919
uint256 public homeChainId;
2020

21-
/// @notice We include the remote chain in the constructor for simplicity. This allows us to
22-
/// retain the same ERC20 api for approve. Otherwise the remote chain id would need to be
23-
/// enshrined in the API -- approve(destinationChainId, spender, ammount). It's fine for the
24-
/// RemoteSuperchainERC20 address to be unique per remote chain since two different remote
25-
/// representations of the same ERC20 across chains do not need to communicate with each other.
21+
/// @notice the remote chain that can hold a lock on this ERC20
2622
uint256 public remoteChainId;
2723

24+
/// @notice the account allowed to acquire this lock from the user via `transferFrom()`
25+
address public spender;
26+
2827
/// @dev The messenger predeploy to handle message passing
2928
IL2ToL2CrossDomainMessenger internal _messenger =
3029
IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
@@ -33,7 +32,8 @@ contract RemoteSuperchainERC20 is ERC20 {
3332
/// @param _homeChainId The chain the ERC20 lives on
3433
/// @param _erc20 The ERC20 token this remote representation is based on
3534
/// @param _remoteChainId The chain this erc20 is controlled by
36-
constructor(uint256 _homeChainId, IERC20 _erc20, uint256 _remoteChainId) ERC20("", "") {
35+
/// @param _spender The account allowed to acquire this lock from the user via `transferFrom()`
36+
constructor(uint256 _homeChainId, IERC20 _erc20, uint256 _remoteChainId, address _spender) ERC20("", "") {
3737
// By asserting the deployer is used, we obtain good safety that
3838
// 1. This contract was deterministically created based on the constructor args
3939
// 2. `approve()` & `transfer()` only works on the correctly erc20 address.
@@ -42,38 +42,44 @@ contract RemoteSuperchainERC20 is ERC20 {
4242
homeChainId = _homeChainId;
4343
erc20 = _erc20;
4444
remoteChainId = _remoteChainId;
45+
spender = _spender;
4546
}
4647

4748
/// @notice Approve a spender on the remote to pull an amout of RemoteSuperchainERC20
4849
/// @param _spender The address of the spender on the remote chain
4950
/// @param _amount The amount to approve
5051
/// @return success True if the approval was successful
5152
function approve(address _spender, uint256 _amount) public override returns (bool) {
52-
require(block.chainid == homeChainId);
53+
if (block.chainid == homeChainId) {
54+
// Resource lock the erc20 on the remote chain
55+
require(_spender == spender);
5356

54-
// (1) Escrow the ERC20
55-
erc20.transferFrom(msg.sender, address(this), _amount);
57+
// (1) Escrow the ERC20
58+
erc20.transferFrom(msg.sender, address(this), _amount);
5659

57-
// (2) Send a message to approve the spender (we reuse the first argument to propogate the holder)
58-
bytes memory call = abi.encodeCall(this.handleApproval, (msg.sender, _spender, _amount));
59-
_messenger.sendMessage(remoteChainId, address(this), call);
60-
return true;
61-
}
60+
// (2) Send a message to approve the spender over the lock (we reuse the first argument to propogate the owner)
61+
bytes memory call = abi.encodeCall(this.approve, (msg.sender, _amount));
62+
_messenger.sendMessage(remoteChainId, address(this), call);
63+
return true;
64+
} else {
65+
// Minting the RemoteERC20 to be acquirable by the spender.
66+
require(block.chainid == remoteChainId);
67+
require(msg.sender == address(_messenger));
6268

63-
/// @notice handle the approval that was made on the home chain
64-
function handleApproval(address _owner, address _spender, uint256 _amount) external {
65-
require(block.chainid == remoteChainId);
66-
require(msg.sender == address(_messenger));
69+
// In this context we're reusing the _sender argument to propogate the owner of the asset.
70+
address owner = _spender;
6771

68-
// (1) Call must have come from the this RemoteERC20
69-
address sender = _messenger.crossDomainMessageSender();
70-
require(sender == address(this));
72+
// (1) Call must have come from the this RemoteERC20
73+
address sender = _messenger.crossDomainMessageSender();
74+
require(sender == address(this));
7175

72-
// (2) Mint the ERC20 to the original owner
73-
super._mint(_owner, _amount);
76+
// (2) Mint the ERC20 to the original owner (re-used _sender argument)
77+
super._mint(owner, _amount);
7478

75-
// (3) Manually set the allowance over the minted tokens for the spender
76-
super._approve(_owner, _spender, _amount);
79+
// (3) Manually set the allowance over the lock for the spender
80+
super._approve(owner, spender, _amount);
81+
return true;
82+
}
7783
}
7884

7985
/// @notice Transfer the approved RemoteSuperchainERC20 to the spender.
@@ -105,7 +111,7 @@ contract RemoteSuperchainERC20 is ERC20 {
105111
// Remotely transfer the ERC20
106112
require(block.chainid == remoteChainId);
107113

108-
// (2) Burn the Remotely Held RemoteERC20
114+
// (2) Burn the Remotely Held RemoteERC20 (either the original owner or the spender)
109115
// @note: If this is the original owner and not the spender, they will still have the allowance set
110116
// on the sender. However, the user would have to manually approve again for there to be any
111117
// tokens for the spender to pull so it is fine for this allowance to remain.

contracts/test/RemoteSuperchainERC20.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ contract RemoteSuperchainERC20Test is StdUtils, Test, Relayer {
3333
function setUp() public virtual {
3434
// home chain is base, remotely controlled by the spender on OPM
3535
bytes memory remoteERC20CreationCode =
36-
abi.encodePacked(type(RemoteSuperchainERC20).creationCode, abi.encode(8453, address(cbBTC), 10));
36+
abi.encodePacked(type(RemoteSuperchainERC20).creationCode, abi.encode(8453, address(cbBTC), 10, spender));
3737

3838
remoteCbBTC = RemoteSuperchainERC20(deployer.computeAddress(salt, keccak256(remoteERC20CreationCode)));
3939

0 commit comments

Comments
 (0)