Skip to content

Commit

Permalink
add arb distributor
Browse files Browse the repository at this point in the history
  • Loading branch information
JoscelynFarr committed Jan 18, 2024
1 parent 4103ea6 commit aca8529
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/MerkleDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;

import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

error AlreadyClaimed();
error InvalidProof();

contract MerkleDistributor {
using SafeERC20 for IERC20;

address public immutable token;
bytes32 public immutable merkleRoot;

// This is a packed array of booleans.
mapping(uint256 => uint256) private claimedBitMap;

// This event is triggered whenever a call to #claim succeeds.
event Claimed(uint256 index, address account, uint256 amount);

constructor(address token_, bytes32 merkleRoot_) {
token = token_;
merkleRoot = merkleRoot_;
}

function isClaimed(uint256 index) public view returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedBitMap[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}

function _setClaimed(uint256 index) private {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
}

function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof)
public
virtual
{
if (isClaimed(index)) revert AlreadyClaimed();

// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(index, account, amount));
if (!MerkleProof.verify(merkleProof, merkleRoot, node)) revert InvalidProof();

// Mark it claimed and send the token.
_setClaimed(index);
IERC20(token).safeTransfer(account, amount);

emit Claimed(index, account, amount);
}
}
31 changes: 31 additions & 0 deletions src/MerkleDistributorWithDeadline.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;

import { MerkleDistributor } from "./MerkleDistributor.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

error EndTimeInPast();
error ClaimWindowFinished();
error NoWithdrawDuringClaim();

contract MerkleDistributorWithDeadline is MerkleDistributor, Ownable {
using SafeERC20 for IERC20;

uint256 public immutable endTime;

constructor(address token_, bytes32 merkleRoot_, uint256 endTime_) MerkleDistributor(token_, merkleRoot_) {
if (endTime_ <= block.timestamp) revert EndTimeInPast();
endTime = endTime_;
}

function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) public override {
if (block.timestamp > endTime) revert ClaimWindowFinished();
super.claim(index, account, amount, merkleProof);
}

function withdraw() external onlyOwner {
if (block.timestamp < endTime) revert NoWithdrawDuringClaim();
IERC20(token).safeTransfer(msg.sender, IERC20(token).balanceOf(address(this)));
}
}
1 change: 1 addition & 0 deletions src/support/FlattenHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "../FundingRateArbitrage.sol";
import "../JOJODealer.sol";
import "../JUSDBank.sol";
import "../Perpetual.sol";
import "../MerkleDistributorWithDeadline.sol";
import "../oracle/EmergencyOracle.sol";
import "../oracle/OracleAdaptor.sol";
import "../subaccount/SubaccountFactory.sol";
Expand Down

0 comments on commit aca8529

Please sign in to comment.