-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(protocol): add EIP-2612 (permit extension) to bridged ERC20 toke…
…ns (#17818) Co-authored-by: dantaik <[email protected]>
- Loading branch information
Showing
6 changed files
with
422 additions
and
1 deletion.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
packages/protocol/contracts/tokenvault/BridgedERC20V2.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import "@openzeppelin/contracts-upgradeable/interfaces/IERC5267Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; | ||
import "./BridgedERC20.sol"; | ||
|
||
/// @title BridgedERC20V2 | ||
/// @notice An upgradeable ERC20 contract that represents tokens bridged from | ||
/// another chain. This implementation adds ERC20Permit support to BridgedERC20. | ||
/// | ||
/// Most of the code were copied from OZ's ERC20PermitUpgradeable.sol contract. | ||
/// | ||
/// @custom:security-contact [email protected] | ||
contract BridgedERC20V2 is BridgedERC20, IERC20PermitUpgradeable, EIP712Upgradeable { | ||
using CountersUpgradeable for CountersUpgradeable.Counter; | ||
|
||
// solhint-disable-next-line var-name-mixedcase | ||
bytes32 private constant _PERMIT_TYPEHASH = keccak256( | ||
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" | ||
); | ||
|
||
mapping(address => CountersUpgradeable.Counter) private _nonces; | ||
uint256[49] private __gap; | ||
|
||
error BTOKEN_DEADLINE_EXPIRED(); | ||
error BTOKEN_INVALID_SIG(); | ||
|
||
/// @inheritdoc IBridgedERC20Initializable | ||
function init( | ||
address _owner, | ||
address _sharedAddressManager, | ||
address _srcToken, | ||
uint256 _srcChainId, | ||
uint8 _decimals, | ||
string calldata _symbol, | ||
string calldata _name | ||
) | ||
external | ||
virtual | ||
override | ||
initializer | ||
{ | ||
// Check if provided parameters are valid | ||
LibBridgedToken.validateInputs(_srcToken, _srcChainId); | ||
__Essential_init(_owner, _sharedAddressManager); | ||
__ERC20_init(_name, _symbol); | ||
__EIP712_init_unchained(_name, "1"); | ||
|
||
// Set contract properties | ||
srcToken = _srcToken; | ||
srcChainId = _srcChainId; | ||
__srcDecimals = _decimals; | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC20PermitUpgradeable | ||
*/ | ||
// solhint-disable-next-line func-name-mixedcase | ||
function DOMAIN_SEPARATOR() external view override returns (bytes32) { | ||
return _domainSeparatorV4(); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC20PermitUpgradeable | ||
*/ | ||
function permit( | ||
address owner, | ||
address spender, | ||
uint256 value, | ||
uint256 deadline, | ||
uint8 v, | ||
bytes32 r, | ||
bytes32 s | ||
) | ||
public | ||
virtual | ||
override | ||
{ | ||
if (block.timestamp > deadline) revert BTOKEN_DEADLINE_EXPIRED(); | ||
|
||
bytes32 structHash = keccak256( | ||
abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline) | ||
); | ||
|
||
bytes32 hash = _hashTypedDataV4(structHash); | ||
|
||
address signer = ECDSAUpgradeable.recover(hash, v, r, s); | ||
if (signer != owner) revert BTOKEN_INVALID_SIG(); | ||
|
||
_approve(owner, spender, value); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC20PermitUpgradeable | ||
*/ | ||
function nonces(address owner) public view virtual override returns (uint256) { | ||
return _nonces[owner].current(); | ||
} | ||
|
||
function supportsInterface(bytes4 _interfaceId) public pure virtual override returns (bool) { | ||
return _interfaceId == type(IERC20PermitUpgradeable).interfaceId | ||
|| _interfaceId == type(IERC5267Upgradeable).interfaceId | ||
|| super.supportsInterface(_interfaceId); | ||
} | ||
|
||
/** | ||
* @dev "Consume a nonce": return the current value and increment. | ||
* | ||
* _Available since v4.1._ | ||
*/ | ||
function _useNonce(address owner) internal virtual returns (uint256 current) { | ||
CountersUpgradeable.Counter storage nonce = _nonces[owner]; | ||
current = nonce.current(); | ||
nonce.increment(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters