@@ -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.
0 commit comments