Untitled

 avatar
unknown
plain_text
a year ago
5.8 kB
10
Indexable

// Interface for distribution strategies
interface IDistributionStrategy {
    function distribute() external;
}

// MasterVault Contract
contract MasterVault is AccessControl {
    IERC20 public ubxToken;

    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    struct Subvault {
        address vaultAddress;
        uint256 allocation;
    }

    Subvault[] public subvaults;
    uint256 public totalAllocations;

    event SubvaultRegistered(address indexed vaultAddress, uint256 allocation);
    event TokensDistributed(address indexed vaultAddress, uint256 amount);

    constructor(IERC20 _ubxToken) {
        ubxToken = _ubxToken;
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(ADMIN_ROLE, msg.sender);
    }

    function registerSubvault(address vaultAddress, uint256 allocation) external onlyRole(ADMIN_ROLE) {
        require(vaultAddress != address(0), "Invalid vault address");
        require(allocation > 0, "Allocation must be greater than zero");

        for (uint256 i = 0; i < subvaults.length; i++) {
            require(subvaults[i].vaultAddress != vaultAddress, "Vault already registered");
        }

        subvaults.push(Subvault(vaultAddress, allocation));
        totalAllocations += allocation;

        emit SubvaultRegistered(vaultAddress, allocation);
    }

    function distributeTokens(uint256 amount) external onlyRole(ADMIN_ROLE) {
        require(totalAllocations > 0, "No subvaults registered");

        for (uint256 i = 0; i < subvaults.length; i++) {
            Subvault storage subvault = subvaults[i];
            uint256 distributeAmount = (amount * subvault.allocation) / totalAllocations;
            require(ubxToken.transfer(subvault.vaultAddress, distributeAmount), "Transfer failed");
            IDistributionStrategy(subvault.vaultAddress).distribute();
            emit TokensDistributed(subvault.vaultAddress, distributeAmount);
        }
    }
}

// PrivateSaleVault Contract
contract PrivateSaleVault is IDistributionStrategy, AccessControl {
    IERC20 public ubxToken;
    uint256 public totalAllocation;
    uint256 public initialReleaseAmount;
    uint256 public vestingStartTime;
    uint256 public vestingDuration;
    uint256 public withdrawInterval;
    uint256 public totalParticipantAllocation;

    struct Participant {
        uint256 allocation;
        uint256 withdrawn;
        uint256 lastWithdrawTime;
    }

    mapping(address => Participant) private participants;

    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    event TokensWithdrawn(address indexed user, uint256 amount);
    event ParticipantAdded(address indexed participant, uint256 allocation);
    event ParticipantRemoved(address indexed participant);

    constructor(
        IERC20 _ubxToken,
        uint256 _totalAllocation,
        uint256 _vestingStartTime,
        uint256 _vestingDuration,
        uint256 _withdrawInterval
    ) {
        ubxToken = _ubxToken;
        totalAllocation = _totalAllocation;
        initialReleaseAmount = (_totalAllocation * 10) / 100; // 10% initial release
        vestingStartTime = _vestingStartTime;
        vestingDuration = _vestingDuration;
        withdrawInterval = _withdrawInterval;

        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(ADMIN_ROLE, msg.sender);
    }

    function addParticipant(address participant, uint256 allocation) external onlyRole(ADMIN_ROLE) {
        require(participant != address(0), "Invalid participant address");
        require(allocation > 0, "Invalid allocation");
        require(participants[participant].allocation == 0, "Participant already added");

        participants[participant] = Participant({
            allocation: allocation,
            withdrawn: 0,
            lastWithdrawTime: vestingStartTime
        });

        totalParticipantAllocation += allocation;

        emit ParticipantAdded(participant, allocation);
    }

    function removeParticipant(address participant) external onlyRole(ADMIN_ROLE) {
        require(participant != address(0), "Invalid participant address");
        require(participants[participant].allocation > 0, "Participant not found");

        totalParticipantAllocation -= participants[participant].allocation;
        delete participants[participant];

        emit ParticipantRemoved(participant);
    }

    function calculateVestedAmount(address participant) public view returns (uint256) {
        if (block.timestamp < vestingStartTime) {
            return 0;
        }

        uint256 initialAmount = (participants[participant].allocation * 10) / 100;
        uint256 elapsedTime = block.timestamp - vestingStartTime;
        uint256 totalVested = initialAmount + ((participants[participant].allocation * 90 * elapsedTime) / (100 * vestingDuration));
        uint256 participantShare = (totalVested * participants[participant].allocation) / totalParticipantAllocation;
        return participantShare;
    }

    function withdrawTokens() external {
        require(block.timestamp >= vestingStartTime, "Vesting period not started");
        require(block.timestamp >= participants[msg.sender].lastWithdrawTime + withdrawInterval, "Withdraw interval not reached");

        uint256 vestedAmount = calculateVestedAmount(msg.sender);
        uint256 withdrawableAmount = vestedAmount - participants[msg.sender].withdrawn;

        require(withdrawableAmount > 0, "No tokens available for withdrawal");

        participants[msg.sender].withdrawn += withdrawableAmount;
        participants[msg.sender].lastWithdrawTime = block.timestamp;

        require(ubxToken.transfer(msg.sender, withdrawableAmount), "Transfer failed");

        emit TokensWithdrawn(msg.sender, withdrawableAmount);
    }
}
Editor is loading...
Leave a Comment