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(nfts): tbz s1 claim diff #18203

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 54 additions & 14 deletions packages/nfts/contracts/trailblazers-airdrop/ERC20Airdrop.sol
Original file line number Diff line number Diff line change
@@ -1,59 +1,99 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/governance/utils/IVotes.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@taiko/blacklist/IMinimalBlacklist.sol";

import "./MerkleClaimable.sol";

/// @title ERC20Airdrop
/// @notice Contract for managing Taiko token airdrop for eligible users.
/// @custom:security-contact security@taiko.xyz
contract ERC20Airdrop is MerkleClaimable {
contract ERC20Airdrop is MerkleClaimable, ReentrancyGuardUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20;

/// @notice The address of the Taiko token contract.
address public token;
IERC20 public token;
/// @notice Blackist address
IMinimalBlacklist public blacklist;

/// @notice Event emitted when the blacklist is updated.
event BlacklistUpdated(address _blacklist);
/// @notice Errors

/// @notice The address of the vault contract.
address public vault;
error ADDRESS_BLACKLISTED();

uint256[48] private __gap;

/// @notice Modifier to check if the address is not blacklisted.
/// @param _address The address to check.
modifier isNotBlacklisted(address _address) {
if (blacklist.isBlacklisted(_address)) revert ADDRESS_BLACKLISTED();
_;
}

/// @notice Initializes the contract.
/// @param _owner The owner of this contract.
/// @param _claimStart The start time of the claim period.
/// @param _claimEnd The end time of the claim period.
/// @param _merkleRoot The merkle root.
/// @param _token The address of the token contract.
/// @param _vault The address of the vault contract.
function init(
address _owner,
uint64 _claimStart,
uint64 _claimEnd,
bytes32 _merkleRoot,
address _token,
address _vault
IERC20 _token,
address _blacklist
)
external
initializer
{
__Essential_init(_owner);
__ReentrancyGuard_init();
__Pausable_init();
__MerkleClaimable_init(_claimStart, _claimEnd, _merkleRoot);

_transferOwnership(_owner == address(0) ? _msgSender() : _owner);
blacklist = IMinimalBlacklist(_blacklist);
token = _token;
vault = _vault;
}

/// @notice Claims the airdrop for the user.
/// @param user The address of the user.
/// @param amount The amount of tokens to claim.
/// @param proof The merkle proof.
function claim(address user, uint256 amount, bytes32[] calldata proof) external nonReentrant {
function claim(
address user,
uint256 amount,
bytes32[] calldata proof
)
external
nonReentrant
isNotBlacklisted(user)
{
// Check if this can be claimed
_verifyClaim(abi.encode(user, amount), proof);

// Transfer the tokens
IERC20(token).safeTransferFrom(vault, user, amount);
// Transfer the tokens from contract
IERC20(token).transfer(user, amount);
}

/// @notice Withdraw ERC20 tokens from the Vault
/// @param _token The ERC20 token address to withdraw
/// @dev Only the owner can execute this function
function withdrawERC20(IERC20 _token) external onlyOwner {
// If token address is address(0), use token
if (address(_token) == address(0)) {
dantaik marked this conversation as resolved.
Show resolved Hide resolved
_token = token;
}
// Transfer the tokens to owner
_token.transfer(owner(), _token.balanceOf(address(this)));
}

/// @notice Internal method to authorize an upgrade
function _authorizeUpgrade(address) internal virtual override onlyOwner { }
}
46 changes: 43 additions & 3 deletions packages/nfts/contracts/trailblazers-airdrop/MerkleClaimable.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "../../../shared/common/EssentialContract.sol";

import { UUPSUpgradeable } from
"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { Ownable2StepUpgradeable } from
"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { ContextUpgradeable } from
"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";

/// @title MerkleClaimable
/// @notice Contract for managing Taiko token airdrop for eligible users
/// @custom:security-contact security@taiko.xyz
abstract contract MerkleClaimable is EssentialContract {
abstract contract MerkleClaimable is
ContextUpgradeable,
UUPSUpgradeable,
Ownable2StepUpgradeable
{
/// @notice Mapping of hashes and their claim status
mapping(bytes32 hash => bool claimed) public isClaimed;

Expand All @@ -26,11 +37,19 @@ abstract contract MerkleClaimable is EssentialContract {
/// @param hash Hash of the claim
event Claimed(bytes32 hash);

/// @notice Event emitted when config is changed
/// @param claimStart Unix timestamp for claim start
/// @param claimEnd Unix timestamp for claim end
/// @param merkleRoot Merkle root of the tree
event ConfigChanged(uint64 claimStart, uint64 claimEnd, bytes32 merkleRoot);

/// @notice Errors
error CLAIM_NOT_ONGOING();
error CLAIMED_ALREADY();
error INVALID_PARAMS();
error INVALID_PROOF();

/// @notice Modifier to check if the claim is ongoing
modifier ongoingClaim() {
if (
merkleRoot == 0x0 || claimStart == 0 || claimEnd == 0 || claimStart > block.timestamp
Expand All @@ -54,6 +73,10 @@ abstract contract MerkleClaimable is EssentialContract {
_setConfig(_claimStart, _claimEnd, _merkleRoot);
}

/// @notice Initialize the contract
/// @param _claimStart Unix timestamp for claim start
/// @param _claimEnd Unix timestamp for claim end
/// @param _merkleRoot Merkle root of the tree
function __MerkleClaimable_init(
uint64 _claimStart,
uint64 _claimEnd,
Expand All @@ -62,9 +85,13 @@ abstract contract MerkleClaimable is EssentialContract {
internal
onlyInitializing
{
__Context_init();
_setConfig(_claimStart, _claimEnd, _merkleRoot);
}

/// @notice Verify an airdrop claim
/// @param data Data to be hashed
/// @param proof Merkle proof
function _verifyClaim(bytes memory data, bytes32[] calldata proof) internal ongoingClaim {
bytes32 hash = keccak256(abi.encode("CLAIM_TAIKO_AIRDROP", data));

Expand All @@ -75,6 +102,11 @@ abstract contract MerkleClaimable is EssentialContract {
emit Claimed(hash);
}

/// @notice Verify a Merkle proof
/// @param _proof Merkle proof
/// @param _merkleRoot Merkle root
/// @param _value Value to verify
/// @return Whether the proof is valid
function _verifyMerkleProof(
bytes32[] calldata _proof,
bytes32 _merkleRoot,
Expand All @@ -88,11 +120,19 @@ abstract contract MerkleClaimable is EssentialContract {
return MerkleProof.verify(_proof, _merkleRoot, _value);
}

/// @notice Set config parameters
/// @param _claimStart Unix timestamp for claim start
/// @param _claimEnd Unix timestamp for claim end
/// @param _merkleRoot Merkle root of the tree
function _setConfig(uint64 _claimStart, uint64 _claimEnd, bytes32 _merkleRoot) private {
if (_claimStart > _claimEnd) revert INVALID_PARAMS();

claimStart = _claimStart;
claimEnd = _claimEnd;
merkleRoot = _merkleRoot;
emit ConfigChanged(_claimStart, _claimEnd, _merkleRoot);
}

/// @notice Internal method to authorize an upgrade
function _authorizeUpgrade(address) internal virtual override onlyOwner { }
}

This file was deleted.

Loading