Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x11bA9e3B2F8afF500E973609c2cfA4B2A0552033
Balance 0 ETH
Nonce 1
Code Size 7388 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

7388 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c80630473d3b4146102345780630a86491a1461022f5780630c08bf881461022a5780630e17454c14610225578063194307bf146102205780631c12691d1461021b57806329315ea7146102165780632e1a7d4d1461021157806336f2e56b1461020c5780634614baaa14610207578063497fb5b9146102025780634c05abeb146101fd57806361d3458f146101f857806363f5de76146101f35780636ccd7214146101ee578063736eb718146101e95780637bd57fbe146101e45780637e1c0c09146101df57806388a17bde146101da5780638c301e57146101d557806397926fc8146101d05780639d0d55b5146101cb578063b7de1cc0146101c6578063baa49301146101c1578063bf7e214f146101bc578063c4083cb1146101b7578063ce4254ce146101b2578063d2487d72146101ad578063d5f52076146101a8578063db9c7d3e146101a3578063e2b178a01461019e578063e89e4ed614610199578063f70eadc6146101945763f77c47911461018f575f80fd5b611532565b6114c1565b611471565b6113f4565b611344565b61131c565b611302565b6112e5565b6112cb565b6112a4565b611233565b611216565b6111fc565b6110f5565b6110d8565b61105e565b610fec565b610e37565b610d97565b610cca565b610ca8565b610c0b565b610be6565b610bc9565b610afa565b610a41565b6108a7565b610673565b61064b565b610626565b61060b565b6103d4565b61031e565b34610309575f8060031936011261030657600c5460ff1661025481610a10565b806102bd5750806003545b80821061029757505061028361027a6102939260045461159d565b600b54906115b8565b6040519081529081906020820190565b0390f35b90916102b16102b7916102a985611434565b50549061159d565b9261158a565b9061025f565b600191506102ca81610a10565b036102e2576102936102dd61027a611b57565b610283565b6102936102dd61027a6102f3611b57565b6102fb611c05565b818110908218021890565b80fd5b5f80fd5b6001600160a01b0381160361030957565b346103095760203660031901126103095760043561033b8161030d565b6001600160a01b037f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b4811633036103c257600780546001600160a01b0319168383161790556001546040516001600160a01b03909316835216907fc72b361adc3106aab68fafd2bb0c88c8640ab9a4a9e44e0fe3c006138dcf4d7b9080602081015b0390a2005b604051635f84f8e360e11b8152600490fd5b34610309575f80600319360112610306577f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b031633036103c25768929eee149b4bd21268308154146105fe57308155600c5460081c60ff166105ec5781826003545b8082106105d45750506104556104639160045461159d565b61045d611b57565b906115b8565b6008546104869061047a906001600160a01b031681565b6001600160a01b031690565b6040516370a0823160e01b80825230600483015260209392918481602481865afa9081156105b25787916105b7575b508311610554575b505090506104ca42600d55565b6008546104eb9082906001600160a01b03165b6104e5611ad3565b9061197f565b6104ff61010061ff0019600c541617600c55565b6001547f0b921cba1f8d7abccbb39feb38b91f62a4b3f2d82fc130481835dd545b7e5d039061054b9061053a906001600160a01b031661047a565b604051938452929081906020820190565b0390a238905580f35b60405190815230600482015291508290829060249082905afa9182156105b2578492610585575b5050805f806104bd565b6105a49250803d106105ab575b61059c8183611669565b81019061168b565b5f8061057b565b503d610592565b61169a565b6105ce9150853d87116105ab5761059c8183611669565b5f6104b5565b90916102b16105e6916102a985611434565b9061043d565b604051637daa23d960e11b8152600490fd5b63ab143c0682526004601cfd5b34610309575f36600319011261030957602060405160018152f35b34610309575f36600319011261030957602060ff600c5460081c166040519015158152f35b34610309575f366003190112610309576002546040516001600160a01b039091168152602090f35b34610309576020806003193601126103095760049081359068929eee149b4bd21268923084541461089c57308455600c5460081c60ff1661088d57600354831091821561087d576106c384611434565b50925f9061086c575b61085c5760408051916106f7836106e98884830160209181520190565b03601f198101855284611669565b60028501805493905f80356001600160e01b03191691905b8681106107ae578a8a8a61074f600182016107338161010061ff0019825416179055565b610748610743845460095461159d565b600955565b5460ff1690565b610793575b5060015461076a906001600160a01b031661047a565b7f232bf093fa1e4fed3729de5722f4210ce4f9652bb817a59794e3dffde53d6fc35f80a3389055005b6107a36107a89154600a5461159d565b600a55565b82610754565b6107f985856107db61047a61047a6107c687896116a5565b905460039190911b1c6001600160a01b031690565b868c8b519586948593849363ba1267b560e01b8552309085016116cf565b03915afa9081156105b2575f9161082f575b501561081f5761081a9061158a565b61070f565b8551637e96811560e01b81528890fd5b61084f9150863d8811610855575b6108478183611669565b8101906116ba565b5f61080b565b503d61083d565b50604051635efff6bb60e01b8152fd5b50600183015460081c60ff166106cc565b5060405163e6cf825560e01b8152fd5b604051637daa23d960e11b8152fd5b63ab143c065f52601cfd5b34610309576020366003190112610309576004803568929eee149b4bd21268913083541461089c573083556001546001600160a01b03919082163303610a015782156109f2576108f5611c76565b83118015610980575b610971575061091761091283600b5461159d565b600b55565b60085461093090839033906001600160a01b031661197f565b600854604051928352166001600160a01b03169033907fcb7000702ccb0f4a36ea88ebadd8460f2535d7780f465abdc42156d59c7e471690602090a3389055005b60405163ae110e3d60e01b8152fd5b506008546109989061047a906001600160a01b031681565b6040516370a0823160e01b815230838201908152909160209183919082908190850103915afa9081156105b2575f916109d4575b5083116108fe565b6109ec915060203d81116105ab5761059c8183611669565b5f6109cc565b6040516317338d0b60e31b8152fd5b604051637232eb7d60e11b8152fd5b60031115610a1a57565b634e487b7160e01b5f52602160045260245ffd5b919060208301926003821015610a1a5752565b34610309575f3660031901126103095761029360ff600c541660405191829182610a2e565b610af89092919260e080610100830195805184526001600160801b03806020830151166020860152604082015116604085015260018060a01b03606082015116606085015265ffffffffffff6080820151166080850152610ad660a082015160a086019060018060a01b03169052565b60c08181015165ffffffffffff169085015201516001600160a01b0316910152565b565b34610309575f806003193601126103065760e0604051610b198161164c565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201520152610293604051610b518161164c565b60045481526005546001600160801b0381166020830152608090811c60408301526006546001600160a01b03808216606085015265ffffffffffff60a092831c81169385019390935260075490811682850152901c1660c08201526008546001600160a01b031660e082015260405191829182610a66565b34610309575f366003190112610309576020600b54604051908152f35b34610309575f36600319011261030957602060405160148152f35b8015150361030957565b3461030957602036600319011261030957600435610c2881610c01565b6001600160a01b03907f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b4821633036103c25760207f2b63f16f8aad8ee3cd55e8dc71af123c4505b84d3db0296fcc922c8ca976455c9115159260025460ff60a01b8560a01b169060ff60a01b1916176002556001541692604051908152a2005b34610309575f366003190112610309576020610cc2611c05565b604051908152f35b3461030957602036600319011261030957600435610ce78161030d565b6001546001600160a01b03908116919033839003610d855781168015610d735760ff60025460a01c1615610d6157610d5f927fd7d74b0ecb9406d169a04ae7ac3ac603f82b97eb19cdfe10f1716c37a80014b05f80a360018060a01b03166bffffffffffffffffffffffff60a01b6002541617600255565b005b6040516347923a9760e11b8152600490fd5b604051630a20411f60e41b8152600490fd5b604051637232eb7d60e11b8152600490fd5b34610309576020366003190112610309576004356003811015610309577f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b031633036103c257600c549060ff8260081c166105ec577f2f117a93d1eaa3374f55da6d5f07d4d62db2e30e2e9679a47f0b80ae2ce014849160ff610e329216809160ff191617600c5560405191829182610a2e565b0390a1005b346103095760031960203682011261030957600480359067ffffffffffffffff908183116103095782810193608084360391820112610309577f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b03163303610fdc5760ff600c5460081c16610fcc576003546014811015610fbb57600160401b811015610fb657806001610ed59201600355611434565b949094610fb15785358555610f3160018601610f0a6024840135610ef881610c01565b829060ff801983541691151516179055565b604483013590610f1982610c01565b9061ff00825491151560081b169061ff001916179055565b60648101359160221901821215610309570190810135918211610309576024018160051b36038113610309576002610f6993016119be565b6001547f877dce72a969310ab9303c0fc5712523d328f5191e944d7959c464f02d5af314906103bd90610fa4906001600160a01b031661047a565b9260405191829182611a54565b611789565b611638565b6040516309bfed0d60e41b81528390fd5b50604051637daa23d960e11b8152fd5b50604051635f84f8e360e11b8152fd5b34610309575f366003190112610309576008546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa80156105b2576020915f91611041575b50604051908152f35b6110589150823d81116105ab5761059c8183611669565b5f611038565b34610309575f3660031901126103095761010060045460055460065460075460018060a01b038060085416936040519586526001600160801b038116602087015260801c6040860152808316606086015265ffffffffffff809360a01c166080860152811660a085015260a01c1660c083015260e0820152f35b34610309575f366003190112610309576020600954604051908152f35b34610309576020366003190112610309576004357f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b031633036103c257600c5460081c60ff166105ec576003548110156111ea5761116f61115c82611434565b50546008546001600160a01b03166104dd565b61118161117b82611434565b9061179c565b6111a76111976111926003546115aa565b611434565b506111a183611434565b90611849565b6111af6118ae565b6001546111c4906001600160a01b031661047a565b7fab7f5ece62808ccb186faae53397a9b1175f6c73f8a1092d7dd3041ba2b9768e5f80a3005b60405163e6cf825560e01b8152600490fd5b34610309575f366003190112610309576020610cc26115c5565b34610309575f366003190112610309576020600a54604051908152f35b34610309575f366003190112610309576002546001600160a01b0381169033829003610d85576bffffffffffffffffffffffff60a01b91808360015416176001557fb87f3d33fa2183dad430f3c1ec9d76f87d9d5220ee47d7a1514c03746e8ed36c6020604051838152a216600255005b34610309575f366003190112610309575f546040516001600160a01b039091168152602090f35b34610309575f366003190112610309576020610cc2611c76565b34610309575f366003190112610309576020600d54604051908152f35b34610309575f366003190112610309576020610cc2611b57565b34610309575f366003190112610309576001546040516001600160a01b039091168152602090f35b34610309576020366003190112610309576004356113618161030d565b6001600160a01b037f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b4811633036103c25760ff600c5460081c166105ec57600680546001600160a01b0319168383161790556001546040516001600160a01b03909316835216907fbf1524e769506b96315e7c8ed4e7bbb9e1184c4527a5dd96b58863d3aacdbf709080602081016103bd565b34610309575f36600319011261030957602061140e611ad3565b6040516001600160a01b039091168152f35b634e487b7160e01b5f52603260045260245ffd5b60035481101561146c57600390815f52027fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01905f90565b611420565b34610309576020366003190112610309576004356003548110156103095761149a606091611434565b5060ff600182549201546040519283528181161515602084015260081c1615156040820152f35b346103095760203660031901126103095760043565ffffffffffff811603610309577f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b031633036103c25760ff600c5460081c166105ec57604051637e96811560e01b8152600490fd5b34610309575f366003190112610309576040517f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b03168152602090f35b634e487b7160e01b5f52601160045260245ffd5b5f1981146115985760010190565b611576565b9190820180921161159857565b5f1981019190821161159857565b9190820391821161159857565b60ff600c5460081c16611634575f806003545b80821061160e575050600b546115ec611c76565b8101809111611598576004549182018092116115985761160b916115b8565b90565b909161161983611434565b505481018091116115985761162e909261158a565b906115d8565b5f90565b634e487b7160e01b5f52604160045260245ffd5b610100810190811067ffffffffffffffff821117610fb657604052565b90601f8019910116810190811067ffffffffffffffff821117610fb657604052565b90816020910312610309575190565b6040513d5f823e3d90fd5b805482101561146c575f5260205f2001905f90565b90816020910312610309575161160b81610c01565b9193929060018060a01b0316825260209363ffffffff60e01b1684830152606060408301528051908160608401525f5b82811061172057505060809293505f838284010152601f8019910116010190565b8181018601518482016080015285016116ff565b8181029291811591840414171561159857565b600160401b8211610fb65780549180825582811061176457505050565b5f9182526020822092830192015b82811061177e57505050565b818155600101611772565b634e487b7160e01b5f525f60045260245ffd5b90610fb1575f808255600260019282848201550190815491818155826117c3575b50505050565b815260208120918201915b828110156117bd5781815583016117ce565b8181146118455781549167ffffffffffffffff8311610fb6576118038383611747565b5f5260205f20905f5260205f208154915f925b848410611824575050505050565b600191820180546001600160a01b0390921684860155939091019290611816565b5050565b90610fb157818103611859575050565b60028083610af8945484556118a66001850160ff6001840161188a82825416849060ff801983541691151516179055565b5460081c1661ff00825491151560081b169061ff001916179055565b0191016117e0565b600354801561196b575f1981019081101561146c575f600381527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85d6003830282817fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0155827fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85c82015501908154918181558261194d575b505050600355565b815260208120918201915b8281101561194557818155600101611958565b634e487b7160e01b5f52603160045260245ffd5b60109260209260145260345260445f9384809363a9059cbb60601b82525af13d1560018351141716156119b157603452565b6390b8ec1890526004601cfd5b9067ffffffffffffffff8311610fb6576119d88383611747565b905f526020805f20905f5b8481106119f1575050505050565b60019082853595611a018761030d565b019481850155016119e3565b9190808252602080920192915f5b828110611a29575050505090565b9091929382806001928735611a3d8161030d565b848060a01b03168152019501910192919092611a1b565b9060208252803560208301526020810135611a6e81610c01565b151560408301526040810135611a8381610c01565b151560608301526060810135601e198236030181121561030957016020813591019067ffffffffffffffff8111610309578060051b360382136103095760a08360808061160b9601520191611a0d565b60405163bf7e214f60e01b81526020816004817f000000000000000000000000b46ce29c2c88b1983386c0a1709feab155d7c0b46001600160a01b03165afa9081156105b2575f91611b23575090565b906020823d8211611b4f575b81611b3c60209383611669565b8101031261030657505161160b8161030d565b3d9150611b2f565b60065465ffffffffffff8160a01c1690814210611bff57814203428111611598579160ff600c5460081c16611be9575b5061160b91611bcc91611ba5916001600160a01b0390911690611734565b611bc6611bba6005546001600160801b031690565b6001600160801b031690565b9061159d565b600454808211611be1575b506009549061159d565b90505f611bd7565b600d549081039250821161159857611ba5611b87565b50505f90565b60075465ffffffffffff8160a01c16804210611bff57420342811161159857611c37916001600160a01b031690611734565b60055460801c81018091116115985780600454906009548201809211611598578110611c6e575b50600a5481018091116115985790565b90505f611c5e565b611c7e611b57565b611c86611c05565b8181109082180218600b548082115f14611bff578103908111611598579056fea26469706673582212202bd4919294f831f70cff95ba8cfbb740d9524ab77c7b3c9ba582f39a4fe4635a64736f6c63430008140033

Verified Source Code Partial Match

Compiler: v0.8.20+commit.a1b79de6 EVM: shanghai Optimization: Yes (200 runs)
VestingAllocation.sol 138 lines
// SPDX-License-Identifier: AGPL-3.0-only
import "./BaseAllocation.sol";

pragma solidity 0.8.20;

contract VestingAllocation is BaseAllocation {

    /// @notice constructor for VestingAllocation
    /// @param _grantee address of the grantee
    /// @param _controller address of the controller
    /// @param _allocation Allocation struct containing token contract
    /// @param _milestones array of Milestone structs with conditions and awards
    constructor (
        address _grantee,
        address _controller,
        Allocation memory _allocation,
        Milestone[] memory _milestones
    ) BaseAllocation(
         _grantee,
         _controller
    ) {
        //perform input validation
        if (_allocation.tokenContract == address(0)) revert MetaVesT_ZeroAddress();
        //if (_allocation.tokenStreamTotal == 0) revert MetaVesT_ZeroAmount();
        if (_grantee == address(0)) revert MetaVesT_ZeroAddress();
        if (_allocation.vestingRate >  1000*1e18 || _allocation.unlockRate > 1000*1e18) revert MetaVesT_RateTooHigh();

        //set vesting allocation variables
        allocation.tokenContract = _allocation.tokenContract;
        allocation.tokenStreamTotal = _allocation.tokenStreamTotal;
        allocation.vestingCliffCredit = _allocation.vestingCliffCredit;
        allocation.unlockingCliffCredit = _allocation.unlockingCliffCredit;
        allocation.vestingRate = _allocation.vestingRate;
        allocation.vestingStartTime = _allocation.vestingStartTime;
        allocation.unlockRate = _allocation.unlockRate;
        allocation.unlockStartTime = _allocation.unlockStartTime;
        // manually copy milestones
        for (uint256 i; i < _milestones.length; ++i) {
            milestones.push(_milestones[i]);
        }
    }

    /// @notice returns the contract vesting type 1 for VestingAllocation
    /// @return 1 for VestingAllocation
    function getVestingType() external pure override returns (uint256) {
        return 1;
    }

    /// @notice returns the governing power of the VestingAllocation
    /// @return governingPower - the governing power of the VestingAllocation based on the governance setting
    function getGoverningPower() external view override returns (uint256 governingPower) {
        if(govType==GovType.all)
        {
            uint256 totalMilestoneAward = 0;
            for(uint256 i; i < milestones.length; ++i)
            { 
                    totalMilestoneAward += milestones[i].milestoneAward;
            }
            governingPower = (allocation.tokenStreamTotal + totalMilestoneAward) - tokensWithdrawn;
        }
        else if(govType==GovType.vested)
             governingPower = getVestedTokenAmount() - tokensWithdrawn;
        else 
            governingPower = _min(getVestedTokenAmount(), getUnlockedTokenAmount()) - tokensWithdrawn;
        
        return governingPower;
    }

    /// @notice unused for VestingAllocation
    /// @dev onlyController -- must be called from the metavest controller
    /// @param _shortStopTime - the new short stop time
    function updateStopTimes(uint48 _shortStopTime) external override onlyController {
        if(terminated) revert MetaVesT_AlreadyTerminated();
        revert MetaVesT_ConditionNotSatisfied();
    }

    /// @notice terminates the VestingAllocation and transfers any remaining tokens to the authority
    /// @dev onlyController -- must be called from the metavest controller
    function terminate() external override onlyController nonReentrant {
        if(terminated) revert MetaVesT_AlreadyTerminated();
        uint256 tokensToRecover = 0;
        uint256 milestonesAllocation = 0;
        for (uint256 i; i < milestones.length; ++i) {
                milestonesAllocation += milestones[i].milestoneAward;
        }
        tokensToRecover = allocation.tokenStreamTotal + milestonesAllocation - getVestedTokenAmount();
        if(tokensToRecover>IERC20M(allocation.tokenContract).balanceOf(address(this)))
            tokensToRecover = IERC20M(allocation.tokenContract).balanceOf(address(this));
        terminationTime = block.timestamp;
        safeTransfer(allocation.tokenContract, getAuthority(), tokensToRecover);
        terminated = true;
        emit MetaVesT_Terminated(grantee, tokensToRecover);
    }

    /// @notice returns the amount of tokens that are vested
    /// @return _tokensVested - the amount of tokens that are vested in decimals of the vesting token
    function getVestedTokenAmount() public view returns (uint256) {
        if(block.timestamp<allocation.vestingStartTime)
            return 0;
        uint256 _timeElapsedSinceVest = block.timestamp - allocation.vestingStartTime;
        if(terminated)
            _timeElapsedSinceVest = terminationTime - allocation.vestingStartTime;

           uint256 _tokensVested = (_timeElapsedSinceVest * allocation.vestingRate) + allocation.vestingCliffCredit;

            if(_tokensVested>allocation.tokenStreamTotal) 
                _tokensVested = allocation.tokenStreamTotal;
        return _tokensVested += milestoneAwardTotal;
    }

    /// @notice returns the amount of tokens that are unlocked
    /// @return _tokensUnlocked - the amount of tokens that are unlocked in decimals of the vesting token
    function getUnlockedTokenAmount() public view returns (uint256) {
        if(block.timestamp<allocation.unlockStartTime)
            return 0;
        uint256 _timeElapsedSinceUnlock = block.timestamp - allocation.unlockStartTime;
        uint256 _tokensUnlocked = (_timeElapsedSinceUnlock * allocation.unlockRate) + allocation.unlockingCliffCredit;

        if(_tokensUnlocked>allocation.tokenStreamTotal + milestoneAwardTotal) 
            _tokensUnlocked = allocation.tokenStreamTotal + milestoneAwardTotal;

        return _tokensUnlocked += milestoneUnlockedTotal;
    }

    /// @notice returns the amount of tokens that are withdrawable
    /// @return _tokensWithdrawable - the amount of tokens that are withdrawable in decimals of the vesting token
    function getAmountWithdrawable() public view override returns (uint256) {
        uint256 _tokensVested = getVestedTokenAmount();
        uint256 _tokensUnlocked = getUnlockedTokenAmount();
        uint256 withdrawableAmount = _min(_tokensVested, _tokensUnlocked);
        if(withdrawableAmount>tokensWithdrawn)
            return withdrawableAmount - tokensWithdrawn;
        else
            return 0;
        
    }

}
BaseAllocation.sol 354 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.20;

/// @notice interface to a MetaLeX condition contract
/// @dev see https://github.com/MetaLex-Tech/BORG-CORE/tree/main/src/libs/conditions
interface IConditionM {
    function checkCondition(address _contract, bytes4 _functionSignature, bytes memory data) external view returns (bool);
}

interface IERC20M {
    function allowance(address owner, address spender) external view returns (uint256);
    function decimals() external view returns (uint8);
    function balanceOf(address account) external view returns (uint256);
}

interface IController { 
    function authority() external view returns (address);
}

/// @notice Solady's SafeTransferLib 'SafeTransfer()' and 'SafeTransferFrom()'; (https://github.com/Vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
abstract contract SafeTransferLib {
    error TransferFailed();
    error TransferFromFailed();

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and(
                    // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. Reverts upon failure.
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and(
                    // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }
}

/// @notice gas-optimized reentrancy protection by Solady (https://github.com/Vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`. 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
    error Reentrancy();

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }
}


abstract contract BaseAllocation is ReentrancyGuard, SafeTransferLib{

        /// @notice MetaVesTController contract address, immutably tied to this MetaVesT
        address public immutable controller;
        uint256 constant public MAX_MILESTONES = 20;
        /// @notice authority address, may replace itself in 'controller'
        address public authority; // REVIEW: probably just have `getAuthority` which calls thru to `controller`? Saves having to worry about updating if it changes?
        struct Milestone {
            uint256 milestoneAward; // per-milestone indexed lump sums of tokens vested upon corresponding milestone completion
            bool unlockOnCompletion; // whether the 'milestoneAward' is to be unlocked upon completion
            bool complete; // whether the Milestone is satisfied and the 'milestoneAward' is to be released
            address[] conditionContracts; // array of contract addresses corresponding to condition(s) that must satisfied for this Milestone to be 'complete'
        }
        error MetaVesT_OnlyController();
        error MetaVesT_OnlyGrantee();
        error MetaVesT_OnlyAuthority();
        error MetaVesT_ZeroAddress();
        error MetaVesT_RateTooHigh();
        error MetaVesT_ZeroAmount();
        error MetaVesT_MilestoneIndexOutOfRange();
        error MetaVesT_NotTerminated();
        error MetaVesT_MilestoneIndexCompletedOrDoesNotExist();
        error MetaVesT_ConditionNotSatisfied();
        error MetaVesT_AlreadyTerminated();
        error MetaVesT_MoreThanAvailable();
        error MetaVesT_VestNotTransferable();
        error MetaVesT_ShortStopTimeNotReached();
        error MetaVest_ShortStopDatePassed();
        error MetaVesT_MaxMilestonesReached();
        error MetaVesT_TooSmallAmount();

        event MetaVesT_MilestoneCompleted(address indexed grantee, uint256 indexed milestoneIndex);
        event MetaVesT_MilestoneAdded(address indexed grantee, Milestone milestone);
        event MetaVesT_MilestoneRemoved(address indexed grantee, uint256 indexed milestoneIndex);
        event MetaVesT_StopTimesUpdated(
            address indexed grant,
            uint48 shortStopTime
        );
        event MetaVesT_TransferabilityUpdated(address indexed grantee, bool isTransferable);
        event MetaVest_TransferRightsPending(address indexed grantee, address indexed pendingGrantee);
        event MetaVesT_TransferredRights(address indexed grantee, address transferee);
        event MetaVesT_UnlockRateUpdated(address indexed grantee, uint208 unlockRate);
        event MetaVesT_VestingRateUpdated(address indexed grantee, uint208 vestingRate);
        event MetaVesT_Withdrawn(address indexed grantee, address indexed tokenAddress, uint256 amount);
        event MetaVesT_PriceUpdated(address indexed grantee, uint256 exercisePrice);
        event MetaVesT_RepurchaseAndWithdrawal(address indexed grantee, address indexed tokenAddress, uint256 withdrawalAmount, uint256 repurchaseAmount);
        event MetaVesT_Terminated(address indexed grantee, uint256 tokensRecovered);
        event MetaVest_GovVariablesUpdated(GovType _govType);

        struct Allocation {
            uint256 tokenStreamTotal; // total number of tokens subject to linear vesting/restriction removal (includes cliff credits but not each 'milestoneAward')
            uint128 vestingCliffCredit; // lump sum of tokens which become vested at 'startTime' and will be added to '_linearVested'
            uint128 unlockingCliffCredit; // lump sum of tokens which become unlocked at 'startTime' and will be added to '_linearUnlocked'
            uint160 vestingRate; // tokens per second that become vested; if RESTRICTED this amount corresponds to 'lapse rate' for tokens that become non-repurchasable
            uint48 vestingStartTime; // if RESTRICTED this amount corresponds to 'lapse start time'
            uint160 unlockRate; // tokens per second that become unlocked;
            uint48 unlockStartTime; // start of the linear unlock
            address tokenContract; // contract address of the ERC20 token included in the MetaVesT
        }

        // enum to determine which tokens in the vesting contract will be counted towards governing power
        enum GovType {all, vested, unlocked}

        address public grantee; // grantee of the tokens
        address public pendingGrantee; // address of the pending grantee
        bool transferable; // whether grantee can transfer their MetaVesT in whole
        Milestone[] public milestones; // array of Milestone structs
        Allocation public allocation; // struct containing vesting and unlocking details
        uint256 public milestoneAwardTotal; // total number of tokens awarded in milestones
        uint256 public milestoneUnlockedTotal; // total number of tokens unlocked in milestones
        uint256 public tokensWithdrawn; // total number of tokens withdrawn
        GovType public govType;
        bool public terminated;
        uint256 public terminationTime;

        /// @notice BaseAllocation constructor
        /// @param _grantee: address of the grantee, cannot be a zero address
        /// @param _controller: address of the MetaVesTController contract
        constructor(address _grantee, address _controller) {
            // Controller can be 0 for an immuatable version, but grantee cannot
            if (_grantee == address(0)) revert MetaVesT_ZeroAddress();
            grantee = _grantee;
            controller = _controller;
            govType = GovType.vested;
        }

        function getVestingType() external view virtual returns (uint256);
        function getGoverningPower() external virtual returns (uint256);  
        function updateStopTimes(uint48 _shortStopTime) external virtual;// onlyController;
        function terminate() external virtual;// onlyController;
        function getAmountWithdrawable() public view virtual returns (uint256);

        /// @notice returns the amount of voting power that may be affected by amendment proposals
        /// @return majorityVotingPower - the amount of tokens that are vested, locked, and unexercised
        function getMajorityVotingPower() external view returns (uint256 majorityVotingPower) {
            //add up the total tokens that are unvested or locked
            if(terminated) return 0;
            uint256 totalMilestoneAward = 0;
            for(uint256 i; i < milestones.length; ++i)
            { 
                    totalMilestoneAward += milestones[i].milestoneAward;
            }
            uint256 tokensNotAffected = tokensWithdrawn + getAmountWithdrawable();
            majorityVotingPower = allocation.tokenStreamTotal + totalMilestoneAward - tokensNotAffected;
        }

        /// @notice updates the transferability of the vesting contract
        /// @dev onlyController -- must be called from the metavest controller
        /// @param _transferable - bool to set the transferability of the vesting contract
        function updateTransferability(bool _transferable) external onlyController {
            transferable = _transferable;
            emit MetaVesT_TransferabilityUpdated(grantee, _transferable);
        }

        function totalTokens() external view returns (uint256) {
            return IERC20M(allocation.tokenContract).balanceOf(address(this));
        }

        /// @notice updates the vesting rate of the VestingAllocation
        /// @dev onlyController -- must be called from the metavest controller
        /// @param _newVestingRate - the updated vesting rate in tokens per second in the vesting token decimal
        function updateVestingRate(uint160 _newVestingRate) external onlyController {
            if(terminated) revert MetaVesT_AlreadyTerminated();
            allocation.vestingRate = _newVestingRate;
            emit MetaVesT_VestingRateUpdated(grantee, _newVestingRate);
        }

        /// @notice updates the unlock rate of the VestingAllocation
        /// @dev onlyController -- must be called from the metavest controller
        /// @param _newUnlockRate - the updated unlock rate in tokens per second in the vesting token decimal
        function updateUnlockRate(uint160 _newUnlockRate) external onlyController {
            allocation.unlockRate = _newUnlockRate;
            emit MetaVesT_UnlockRateUpdated(grantee, _newUnlockRate);
        }

        /// @notice Sets the governing power type for the MetaVesT
        /// @param _govType: the type of governing power to be used
        function setGovVariables(GovType _govType) external onlyController {
            if(terminated) revert MetaVesT_AlreadyTerminated();
            govType = _govType;
            emit MetaVest_GovVariablesUpdated(govType);
        }

        /// @notice allows a milestone to be 'unlocked'. callable by anyone but the conditions for the milestone must be met
        /// @param _milestoneIndex - the index of the milestone to confirm
        function confirmMilestone(uint256 _milestoneIndex) external nonReentrant {
            if(terminated) revert MetaVesT_AlreadyTerminated();
            if(_milestoneIndex >= milestones.length) revert MetaVesT_MilestoneIndexOutOfRange();
            Milestone storage milestone = milestones[_milestoneIndex];
            if (_milestoneIndex >= milestones.length || milestone.complete)
                revert MetaVesT_MilestoneIndexCompletedOrDoesNotExist();
            
            //encode the milestone index to bytes for signature verification
            bytes memory _data = abi.encodePacked(_milestoneIndex);
            // perform any applicable condition checks, including whether 'authority' has a signatureCondition
            for (uint256 i; i < milestone.conditionContracts.length; ++i) {
                if (!IConditionM(milestone.conditionContracts[i]).checkCondition(address(this), msg.sig, _data))
                    revert MetaVesT_ConditionNotSatisfied();
            }

            milestone.complete = true;
            milestoneAwardTotal += milestone.milestoneAward;
            if(milestone.unlockOnCompletion)
                milestoneUnlockedTotal += milestone.milestoneAward;
        
            emit MetaVesT_MilestoneCompleted(grantee, _milestoneIndex);
        }

        /// @notice removes a milestone from the VestingAllocation
        /// @dev onlyController -- must be called from the metavest controller
        /// @param _milestoneIndex - the index of the milestone to remove
        function removeMilestone(uint256 _milestoneIndex) external onlyController {
            if(terminated) revert MetaVesT_AlreadyTerminated();
            if (_milestoneIndex >= milestones.length) revert MetaVesT_MilestoneIndexOutOfRange();
            uint256 _milestoneAward = milestones[_milestoneIndex].milestoneAward;
            //transfer the milestone award back to the authority, we check in the controller to ensure only uncompleted milestones can be removed
            safeTransfer(allocation.tokenContract, getAuthority(), _milestoneAward);
            delete milestones[_milestoneIndex];
            milestones[_milestoneIndex] = milestones[milestones.length - 1];
            milestones.pop();
            emit MetaVesT_MilestoneRemoved(grantee, _milestoneIndex);
        }

        /// @notice adds a milestone to the VestingAllocation
        /// @dev onlyController -- must be called from the metavest controller
        /// @param _milestone - the milestone to add
        function addMilestone(Milestone calldata _milestone) external onlyController {
            if(terminated) revert MetaVesT_AlreadyTerminated();
            if(milestones.length >= MAX_MILESTONES) revert MetaVesT_MaxMilestonesReached();
            milestones.push(_milestone);
            emit MetaVesT_MilestoneAdded(grantee, _milestone);
        }

        /// @notice transfers the rights of the VestingAllocation to a new owner
        /// @dev onlyGrantee -- must be called by the grantee
        /// @param _newOwner - the address of the new owner
        function transferRights(address _newOwner) external onlyGrantee {
            if(_newOwner == address(0)) revert MetaVesT_ZeroAddress();
            if(!transferable) revert MetaVesT_VestNotTransferable();
            emit MetaVest_TransferRightsPending(grantee, _newOwner);
            pendingGrantee = _newOwner;
        }

        /// @notice confirms the transfer of the rights of the VestingAllocation to a new owner
        function confirmTransfer() external {
            if(msg.sender != pendingGrantee) revert MetaVesT_OnlyGrantee();
            grantee = pendingGrantee;
            emit MetaVesT_TransferredRights(grantee, pendingGrantee);
            pendingGrantee = address(0);
        }

        /// @notice withdraws tokens from the VestingAllocation
        /// @dev onlyGrantee -- must be called by the grantee
        /// @param _amount - the amount of tokens to withdraw
        function withdraw(uint256 _amount) external nonReentrant onlyGrantee {
            if (_amount == 0) revert MetaVesT_ZeroAmount();
            if (_amount > getAmountWithdrawable() || _amount > IERC20M(allocation.tokenContract).balanceOf(address(this))) revert MetaVesT_MoreThanAvailable();
            tokensWithdrawn += _amount;
            safeTransfer(allocation.tokenContract, msg.sender, _amount);
            emit MetaVesT_Withdrawn(msg.sender, allocation.tokenContract, _amount);
        }

        /// @notice gets the details of the vest
        /// @return Allocation - the allocation details
        function getMetavestDetails() external view returns (Allocation memory) {
            return allocation;
        }

        /// @notice returns the authority address
        /// @return address of the authority
        function getAuthority() public view returns (address){
            return IController(controller).authority();
        }
        
        modifier onlyController() {
            if (msg.sender != controller) revert MetaVesT_OnlyController();
            _;
        }

        modifier onlyGrantee() {
            if (msg.sender != grantee) revert MetaVesT_OnlyGrantee();
            _;
        }

        modifier onlyAuthority() {
            if (msg.sender != getAuthority()) revert MetaVesT_OnlyAuthority();
            _;
        }

        /// @dev returns the minimum of `x` and `y`. See https://github.com/Vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol
        function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
                }
        }


}

Read Contract

MAX_MILESTONES 0x4c05abeb → uint256
allocation 0x88a17bde → uint256, uint128, uint128, uint160, uint48, uint160, uint48, address
authority 0xbf7e214f → address
controller 0xf77c4791 → address
getAmountWithdrawable 0xc4083cb1 → uint256
getAuthority 0xe2b178a0 → address
getGoverningPower 0x0473d3b4 → uint256
getMajorityVotingPower 0x9d0d55b5 → uint256
getMetavestDetails 0x4614baaa → tuple
getUnlockedTokenAmount 0x63f5de76 → uint256
getVestedTokenAmount 0xd2487d72 → uint256
getVestingType 0x0e17454c → uint256
govType 0x36f2e56b → uint8
grantee 0xd5f52076 → address
milestoneAwardTotal 0x8c301e57 → uint256
milestoneUnlockedTotal 0xb7de1cc0 → uint256
milestones 0xe89e4ed6 → uint256, bool, bool
pendingGrantee 0x1c12691d → address
terminated 0x194307bf → bool
terminationTime 0xce4254ce → uint256
tokensWithdrawn 0x497fb5b9 → uint256
totalTokens 0x7e1c0c09 → uint256

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

addMilestone 0x52ca220a
tuple _milestone
confirmMilestone 0x29315ea7
uint256 _milestoneIndex
confirmTransfer 0xbaa49301
No parameters
removeMilestone 0x97926fc8
uint256 _milestoneIndex
setGovVariables 0x736eb718
uint8 _govType
terminate 0x0c08bf88
No parameters
transferRights 0x6ccd7214
address _newOwner
updateStopTimes 0xf70eadc6
uint48 _shortStopTime
updateTransferability 0x61d3458f
bool _transferable
updateUnlockRate 0x0a86491a
uint160 _newUnlockRate
updateVestingRate 0xdb9c7d3e
uint160 _newVestingRate
withdraw 0x2e1a7d4d
uint256 _amount

Recent Transactions

No transactions found for this address