Skip to content

Commit

Permalink
feat(protocol): enable EIP712 signature for TimelockTokenPool (#16335)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik committed Mar 6, 2024
1 parent d6ef79e commit d93e4c5
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 10 deletions.
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");

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

0 comments on commit d93e4c5

Please sign in to comment.