Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): enable EIP712 signature for TimelockTokenPool #16335

Merged
merged 15 commits into from
Mar 6, 2024
Merged
2 changes: 1 addition & 1 deletion packages/protocol/contracts/common/EssentialContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ abstract contract EssentialContract is UUPSUpgradeable, Ownable2StepUpgradeable,
}

/// @notice Returns true if the contract is paused, and false otherwise.
/// @return True if paused, false otherwise.
/// @return true if paused, false otherwise.
function paused() public view returns (bool) {
return __paused == _TRUE;
}
Expand Down
27 changes: 19 additions & 8 deletions packages/protocol/contracts/team/TimelockTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.24;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "../common/EssentialContract.sol";

/// @title TimelockTokenPool
Expand All @@ -22,7 +23,7 @@ import "../common/EssentialContract.sol";
/// - team members, advisors, etc.
/// - grant program grantees
/// @custom:security-contact security@taiko.xyz
contract TimelockTokenPool is EssentialContract {
contract TimelockTokenPool is EssentialContract, EIP712Upgradeable {
using SafeERC20 for IERC20;

struct Grant {
Expand Down Expand Up @@ -55,6 +56,8 @@ contract TimelockTokenPool is EssentialContract {
Grant grant;
}

bytes32 public constant TYPED_HASH = keccak256("Withdrawal(address to)");

/// @notice The Taiko token address.
address public taikoToken;

Expand Down Expand Up @@ -118,6 +121,8 @@ contract TimelockTokenPool is EssentialContract {
initializer
{
__Essential_init(_owner);
__EIP712_init("Taiko TimelockTokenPool", "1");
dantaik marked this conversation as resolved.
Show resolved Hide resolved

if (_taikoToken == address(0)) revert INVALID_PARAM();
taikoToken = _taikoToken;

Expand All @@ -132,7 +137,7 @@ contract TimelockTokenPool is EssentialContract {
/// This transaction should happen on a regular basis, e.g., quarterly.
/// @param _recipient The grant recipient address.
/// @param _grant The grant struct.
function grant(address _recipient, Grant memory _grant) external onlyOwner {
function grant(address _recipient, Grant memory _grant) external onlyOwner nonReentrant {
if (_recipient == address(0)) revert INVALID_PARAM();
if (recipients[_recipient].grant.amount != 0) revert ALREADY_GRANTED();

Expand All @@ -147,7 +152,7 @@ contract TimelockTokenPool is EssentialContract {
/// granted to the recipient will NOT be voided but are subject to the
/// original unlock schedule.
/// @param _recipient The grant recipient address.
function void(address _recipient) external onlyOwner {
function void(address _recipient) external onlyOwner nonReentrant {
Recipient storage r = recipients[_recipient];
uint128 amountVoided = _voidGrant(r.grant);

Expand All @@ -158,20 +163,26 @@ contract TimelockTokenPool is EssentialContract {
}

/// @notice Withdraws all withdrawable tokens.
function withdraw() external {
function withdraw() external nonReentrant {
_withdraw(msg.sender, msg.sender);
}

/// @notice Withdraws all withdrawable tokens.
/// @notice Withdraws all withdrawable tokens to a designated address.
/// @param _to The address where the granted and unlocked tokens shall be sent to.
/// @param _sig Signature provided by the grant recipient.
function withdraw(address _to, bytes memory _sig) external {
function withdraw(address _to, bytes memory _sig) external nonReentrant {
if (_to == address(0)) revert INVALID_PARAM();
bytes32 hash = keccak256(abi.encodePacked("Withdraw unlocked Taiko token to: ", _to));
address recipient = ECDSA.recover(hash, _sig);
address recipient = ECDSA.recover(getWithdrawalHash(_to), _sig);
_withdraw(recipient, _to);
}

/// @notice Gets the hash to be signed to authorize an withdrawal.
/// @param _to The destination address.
/// @return The hash to be signed.
function getWithdrawalHash(address _to) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(TYPED_HASH, _to)));
}

/// @notice Returns the summary of the grant for a given recipient.
function getMyGrantSummary(address _recipient)
public
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/team/airdrop/ERC20Airdrop2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ contract ERC20Airdrop2 is MerkleClaimable {

/// @notice External withdraw function
/// @param user User address
function withdraw(address user) external ongoingWithdrawals {
function withdraw(address user) external ongoingWithdrawals nonReentrant {
(, uint256 amount) = getBalance(user);
withdrawnAmount[user] += amount;
IERC20(token).safeTransferFrom(vault, user, amount);
Expand Down
Loading