-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
658 additions
and
0 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
packages/arcana/deploy/lineaGoerli/003_deploy_ERC20Distributor.ts
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,19 @@ | ||
import { DeployFunction } from 'hardhat-deploy/types'; | ||
|
||
const func: DeployFunction = async function ({ deployments, getNamedAccounts }) { | ||
const { deploy } = deployments; | ||
|
||
const { deployer } = await getNamedAccounts(); | ||
|
||
await deploy('ERC20Distributor', { | ||
from: deployer, | ||
// self deployed USDT | ||
// https://goerli.lineascan.build/address/0x7757f3b0d9270bea2f28366a97cdec36a2de3459 | ||
args: [deployer, '0x7757f3b0d9270bea2f28366a97cdec36a2de3459'], | ||
log: true, | ||
}); | ||
}; | ||
|
||
func.tags = ['ERC20Distributor']; | ||
|
||
export default func; |
521 changes: 521 additions & 0 deletions
521
packages/arcana/deployments/lineaGoerli/ERC20Distributor.json
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity 0.8.19; | ||
|
||
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; | ||
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; | ||
import {Address} from "@openzeppelin/contracts/utils/Address.sol"; | ||
import {IERC20Distributor} from "src/interfaces/IERC20Distributor.sol"; | ||
|
||
contract ERC20Distributor is IERC20Distributor, Ownable2Step { | ||
using BitMaps for BitMaps.BitMap; | ||
using SafeERC20 for IERC20; | ||
|
||
IERC20 public rewardToken; | ||
bytes32 public merkleRoot; | ||
uint256 public claimPeriodEnds; | ||
BitMaps.BitMap private claimed; | ||
|
||
constructor(address owner_, IERC20 rewardToken_) { | ||
_transferOwnership(owner_); | ||
rewardToken = rewardToken_; | ||
} | ||
|
||
/** | ||
* @dev Claims airdropped tokens. | ||
* @param amount The amount of the claim being made. | ||
* @param merkleProof A merkle proof proving the claim is valid. | ||
*/ | ||
function claimTokens( | ||
uint256 amount, | ||
bytes32[] calldata merkleProof | ||
) external { | ||
if (block.timestamp >= claimPeriodEnds) { | ||
revert ClaimPeriodNotStartOrEnd(); | ||
} | ||
|
||
bytes32 leaf = keccak256( | ||
bytes.concat(keccak256(abi.encode(msg.sender, amount))) | ||
); | ||
bool valid = MerkleProof.verify(merkleProof, merkleRoot, leaf); | ||
|
||
if (!valid) { | ||
revert InvalidProof(); | ||
} | ||
if (isClaimed(_msgSender())) { | ||
revert AlreadyClaimed(); | ||
} | ||
|
||
claimed.set(uint160(_msgSender())); | ||
rewardToken.safeTransfer(_msgSender(),amount); | ||
emit Claim(msg.sender, amount); | ||
} | ||
|
||
/** | ||
* @dev Returns true if the claim at the given index in the merkle tree has already been made. | ||
* @param user The index into the merkle tree. | ||
*/ | ||
function isClaimed(address user) public view returns (bool) { | ||
return claimed.get(uint256(uint160(user))); | ||
} | ||
|
||
/** | ||
* @dev Sets the merkle root. | ||
* @notice allow set twice here | ||
* @param newMerkleRoot The merkle root to set. | ||
*/ | ||
function setMerkleRoot(bytes32 newMerkleRoot) external onlyOwner { | ||
if (newMerkleRoot == bytes32(0)) { | ||
revert ZeroRootSet(); | ||
} | ||
merkleRoot = newMerkleRoot; | ||
emit MerkleRootChanged(merkleRoot); | ||
} | ||
|
||
/** | ||
* @dev Sets the claim period ends. | ||
* @param claimPeriodEnds_ The merkle root to set. | ||
*/ | ||
function setClaimPeriodEnds(uint256 claimPeriodEnds_) external onlyOwner { | ||
if (claimPeriodEnds_ <= block.timestamp) { | ||
revert InvalidTimestamp(); | ||
} | ||
claimPeriodEnds = claimPeriodEnds_; | ||
emit ClaimPeriodEndsChanged(claimPeriodEnds); | ||
} | ||
|
||
/** | ||
* @dev withdraw remaining native tokens. | ||
*/ | ||
function withdraw(address to) external onlyOwner { | ||
uint256 balance = rewardToken.balanceOf(address(this)); | ||
rewardToken.safeTransfer(to,balance); | ||
emit Withdrawn(to, balance); | ||
} | ||
|
||
} |
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,20 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity 0.8.19; | ||
|
||
interface IERC20DistributorDef { | ||
error ZeroAddressSet(); | ||
error ClaimPeriodNotStartOrEnd(); | ||
error InvalidProof(); | ||
error AlreadyClaimed(); | ||
error ZeroRootSet(); | ||
error InvalidTimestamp(); | ||
|
||
event Claim(address indexed claimant, uint256 amount); | ||
|
||
event MerkleRootChanged(bytes32 merkleRoot); | ||
event ClaimPeriodEndsChanged(uint256 claimPeriodEnds); | ||
event Withdrawn(address dest, uint256 amount); | ||
|
||
} | ||
|
||
interface IERC20Distributor is IERC20DistributorDef {} |