all files / contracts/mining/ EulDistributor.sol

100% Statements 20/20
100% Branches 12/12
100% Functions 5/5
100% Lines 22/22
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                                                120× 120× 120× 120×           23× 11×                                 404× 404×   300× 300×   274× 274×       274×   274× 174×   100× 80× 40×        
// SPDX-License-Identifier: GPL-2.0-or-later
 
pragma solidity ^0.8.0;
 
import "../vendor/MerkleProof.sol";
import "../Utils.sol";
 
interface IEulStakes {
    function stakeGift(address beneficiary, address underlying, uint amount) external;
}
 
contract EulDistributor {
    address public immutable eul;
    address public immutable eulStakes;
    string public constant name = "EUL Distributor";
 
    address public owner;
    bytes32 public currRoot;
    bytes32 public prevRoot;
    mapping(address => mapping(address => uint)) public claimed; // account -> token -> amount
 
    event OwnerChanged(address indexed newOwner);
 
    constructor(address eul_, address eulStakes_) {
        eul = eul_;
        eulStakes = eulStakes_;
        owner = msg.sender;
        Utils.safeApprove(eul_, eulStakes_, type(uint).max);
    }
 
    // Owner functions
 
    modifier onlyOwner {
        require(msg.sender == owner, "unauthorized");
        _;
    }
 
    function transferOwnership(address newOwner) external onlyOwner {
        owner = newOwner;
        emit OwnerChanged(newOwner);
    }
 
    function updateRoot(bytes32 newRoot) external onlyOwner {
        prevRoot = currRoot;
        currRoot = newRoot;
    }
 
    // Claiming
 
    /// @notice Claim distributed tokens
    /// @param account Address that should receive tokens
    /// @param token Address of token being claimed (ie EUL)
    /// @param proof Merkle proof that validates this claim
    /// @param stake If non-zero, then the address of a token to auto-stake to, instead of claiming
    function claim(address account, address token, uint claimable, bytes32[] calldata proof, address stake) external {
        bytes32 candidateRoot = MerkleProof.processProof(proof, keccak256(abi.encodePacked(account, token, claimable))); // 72 byte leaf
        require(candidateRoot == currRoot || candidateRoot == prevRoot, "proof invalid/expired");
 
        uint alreadyClaimed = claimed[account][token];
        require(claimable > alreadyClaimed, "already claimed");
 
        uint amount;
        unchecked {
            amount = claimable - alreadyClaimed;
        }
 
        claimed[account][token] = claimable;
 
        if (stake == address(0)) {
            Utils.safeTransfer(token, account, amount);
        } else {
            require(msg.sender == account, "can only auto-stake for yourself");
            require(token == eul, "can only auto-stake EUL");
            IEulStakes(eulStakes).stakeGift(account, stake, amount);
        }
    }
}