Skip to content

Commit 6e6808d

Browse files
committed
feat: make StarknetTokenBridge ERC2771 compliant
1 parent d62a255 commit 6e6808d

File tree

4 files changed

+120
-15
lines changed

4 files changed

+120
-15
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ Note: the frontend implementation of the bridges, can be found [here](https://gi
3535

3636
This project contains scripts written in Python 3.9.
3737

38+
## EIP2771 compliance
39+
40+
We made the L1 bridge EIP2771 compliant by inheriting from [ERC2771Recipient.sol](https://github.com/opengsn/gsn/blob/master/packages/contracts/src/ERC2771Recipient.sol).
41+
3842

3943
## Getting Started
4044

src/solidity/ERC2771Recipient.sol

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-License-Identifier: MIT
2+
// solhint-disable no-inline-assembly
3+
pragma solidity >=0.6.9;
4+
5+
import "./IERC2771Recipient.sol";
6+
7+
/**
8+
* @title The ERC-2771 Recipient Base Abstract Class - Implementation
9+
*
10+
* @notice Note that this contract was called `BaseRelayRecipient` in the previous revision of the GSN.
11+
*
12+
* @notice A base contract to be inherited by any contract that want to receive relayed transactions.
13+
*
14+
* @notice A subclass must use `_msgSender()` instead of `msg.sender`.
15+
*/
16+
abstract contract ERC2771Recipient is IERC2771Recipient {
17+
18+
/*
19+
* Forwarder singleton we accept calls from
20+
*/
21+
address private _trustedForwarder;
22+
23+
/**
24+
* :warning: **Warning** :warning: The Forwarder can have a full control over your Recipient. Only trust verified Forwarder.
25+
* @notice Method is not a required method to allow Recipients to trust multiple Forwarders. Not recommended yet.
26+
* @return forwarder The address of the Forwarder contract that is being used.
27+
*/
28+
function getTrustedForwarder() public virtual view returns (address forwarder){
29+
return _trustedForwarder;
30+
}
31+
32+
function _setTrustedForwarder(address _forwarder) internal {
33+
_trustedForwarder = _forwarder;
34+
}
35+
36+
/// @inheritdoc IERC2771Recipient
37+
function isTrustedForwarder(address forwarder) public virtual override view returns(bool) {
38+
return forwarder == _trustedForwarder;
39+
}
40+
41+
/// @inheritdoc IERC2771Recipient
42+
function _msgSender() internal override virtual view returns (address ret) {
43+
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
44+
// At this point we know that the sender is a trusted forwarder,
45+
// so we trust that the last bytes of msg.data are the verified sender address.
46+
// extract sender address from the end of msg.data
47+
assembly {
48+
ret := shr(96,calldataload(sub(calldatasize(),20)))
49+
}
50+
} else {
51+
ret = msg.sender;
52+
}
53+
}
54+
55+
/// @inheritdoc IERC2771Recipient
56+
function _msgData() internal override virtual view returns (bytes calldata ret) {
57+
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
58+
return msg.data[0:msg.data.length-20];
59+
} else {
60+
return msg.data;
61+
}
62+
}
63+
}

src/solidity/IERC2771Recipient.sol

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.0;
3+
4+
/**
5+
* @title The ERC-2771 Recipient Base Abstract Class - Declarations
6+
*
7+
* @notice A contract must implement this interface in order to support relayed transaction.
8+
*
9+
* @notice It is recommended that your contract inherits from the ERC2771Recipient contract.
10+
*/
11+
abstract contract IERC2771Recipient {
12+
13+
/**
14+
* :warning: **Warning** :warning: The Forwarder can have a full control over your Recipient. Only trust verified Forwarder.
15+
* @param forwarder The address of the Forwarder contract that is being used.
16+
* @return isTrustedForwarder `true` if the Forwarder is trusted to forward relayed transactions by this Recipient.
17+
*/
18+
function isTrustedForwarder(address forwarder) public virtual view returns(bool);
19+
20+
/**
21+
* @notice Use this method the contract anywhere instead of msg.sender to support relayed transactions.
22+
* @return sender The real sender of this call.
23+
* For a call that came through the Forwarder the real sender is extracted from the last 20 bytes of the `msg.data`.
24+
* Otherwise simply returns `msg.sender`.
25+
*/
26+
function _msgSender() internal virtual view returns (address);
27+
28+
/**
29+
* @notice Use this method in the contract instead of `msg.data` when difference matters (hashing, signature, etc.)
30+
* @return data The real `msg.data` of this call.
31+
* For a call that came through the Forwarder, the real sender address was appended as the last 20 bytes
32+
* of the `msg.data` - so this method will strip those 20 bytes off.
33+
* Otherwise (if the call was made directly and not through the forwarder) simply returns `msg.data`.
34+
*/
35+
function _msgData() internal virtual view returns (bytes calldata);
36+
}

src/solidity/StarknetTokenBridge.sol

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "starkware/solidity/libraries/Transfers.sol";
99
import "starkware/solidity/tokens/ERC20/IERC20.sol";
1010
import "starkware/solidity/tokens/ERC20/IERC20Metadata.sol";
1111
import "starkware/starknet/solidity/IStarknetMessaging.sol";
12+
import "src/solidity/ERC2771Recipient.sol";
1213
import "src/solidity/Fees.sol";
1314
import "src/solidity/IStarkgateBridge.sol";
1415
import "src/solidity/IStarkgateManager.sol";
@@ -25,7 +26,8 @@ contract StarknetTokenBridge is
2526
IStarkgateService,
2627
Identity,
2728
StarknetTokenStorage,
28-
ProxySupport
29+
ProxySupport,
30+
ERC2771Recipient
2931
{
3032
using Addresses for address;
3133
using Felt252 for string;
@@ -125,7 +127,7 @@ contract StarknetTokenBridge is
125127
}
126128

127129
modifier onlyManager() {
128-
require(manager() == msg.sender, "ONLY_MANAGER");
130+
require(manager() == _msgSender(), "ONLY_MANAGER");
129131
_;
130132
}
131133

@@ -152,7 +154,7 @@ contract StarknetTokenBridge is
152154
Fees.checkDepositFee(msg.value);
153155
uint256 currentBalance = IERC20(token).balanceOf(address(this));
154156
require(currentBalance + amount <= getMaxTotalBalance(token), "MAX_BALANCE_EXCEEDED");
155-
Transfers.transferIn(token, msg.sender, amount);
157+
Transfers.transferIn(token, _msgSender(), amount);
156158
return msg.value;
157159
}
158160

@@ -320,10 +322,10 @@ contract StarknetTokenBridge is
320322
uint256 fee
321323
) internal {
322324
if (selector == HANDLE_TOKEN_DEPOSIT_SELECTOR) {
323-
emit Deposit(msg.sender, token, amount, l2Recipient, nonce, fee);
325+
emit Deposit(_msgSender(), token, amount, l2Recipient, nonce, fee);
324326
} else {
325327
require(selector == HANDLE_DEPOSIT_WITH_MESSAGE_SELECTOR, "UNKNOWN_SELECTOR");
326-
emit DepositWithMessage(msg.sender, token, amount, l2Recipient, message, nonce, fee);
328+
emit DepositWithMessage(_msgSender(), token, amount, l2Recipient, message, nonce, fee);
327329
}
328330
}
329331

@@ -339,15 +341,15 @@ contract StarknetTokenBridge is
339341
*/
340342
function enableWithdrawalLimit(address token) external onlySecurityAgent {
341343
tokenSettings()[token].withdrawalLimitApplied = true;
342-
emit WithdrawalLimitEnabled(msg.sender, token);
344+
emit WithdrawalLimitEnabled(_msgSender(), token);
343345
}
344346

345347
/**
346348
Unset withdrawal limit for a token.
347349
*/
348350
function disableWithdrawalLimit(address token) external onlySecurityAdmin {
349351
tokenSettings()[token].withdrawalLimitApplied = false;
350-
emit WithdrawalLimitDisabled(msg.sender, token);
352+
emit WithdrawalLimitDisabled(_msgSender(), token);
351353
}
352354

353355
/**
@@ -397,7 +399,7 @@ contract StarknetTokenBridge is
397399
: N_DEPOSIT_PAYLOAD_ARGS;
398400
uint256[] memory payload = new uint256[](MESSAGE_OFFSET + message.length);
399401
payload[0] = uint256(uint160(token));
400-
payload[1] = uint256(uint160(msg.sender));
402+
payload[1] = uint256(uint160(_msgSender()));
401403
payload[2] = l2Recipient;
402404
payload[3] = amount & (UINT256_PART_SIZE - 1);
403405
payload[4] = amount >> UINT256_PART_SIZE_BITS;
@@ -499,7 +501,7 @@ contract StarknetTokenBridge is
499501
}
500502

501503
function withdraw(address token, uint256 amount) external {
502-
withdraw(token, amount, msg.sender);
504+
withdraw(token, amount, _msgSender());
503505
}
504506

505507
/*
@@ -525,7 +527,7 @@ contract StarknetTokenBridge is
525527
nonce
526528
);
527529

528-
emit DepositCancelRequest(msg.sender, token, amount, l2Recipient, nonce);
530+
emit DepositCancelRequest(_msgSender(), token, amount, l2Recipient, nonce);
529531
}
530532

531533
/*
@@ -552,7 +554,7 @@ contract StarknetTokenBridge is
552554
);
553555

554556
emit DepositWithMessageCancelRequest(
555-
msg.sender,
557+
_msgSender(),
556558
token,
557559
amount,
558560
l2Recipient,
@@ -581,8 +583,8 @@ contract StarknetTokenBridge is
581583
nonce
582584
);
583585

584-
transferOutFunds(token, amount, msg.sender);
585-
emit DepositWithMessageReclaimed(msg.sender, token, amount, l2Recipient, message, nonce);
586+
transferOutFunds(token, amount, _msgSender());
587+
emit DepositWithMessageReclaimed(_msgSender(), token, amount, l2Recipient, message, nonce);
586588
}
587589

588590
function depositReclaim(
@@ -598,7 +600,7 @@ contract StarknetTokenBridge is
598600
nonce
599601
);
600602

601-
transferOutFunds(token, amount, msg.sender);
602-
emit DepositReclaimed(msg.sender, token, amount, l2Recipient, nonce);
603+
transferOutFunds(token, amount, _msgSender());
604+
emit DepositReclaimed(_msgSender(), token, amount, l2Recipient, nonce);
603605
}
604606
}

0 commit comments

Comments
 (0)