A comprehensive decentralized token streaming and vesting service built for HyperEVM, supporting both ERC20 tokens and native HYPE with flexible stream management and cost-efficient deployment.
- ✅ ERC20 & Native HYPE support – Stream either fungible tokens or chain-native currency
- ✅ Multiple streams per user – One beneficiary can have multiple active schedules
- ✅ Merkle-proof based streams – Bulk vesting with lightweight claims
- ✅ Editable & Cancelable streams – Owners can modify or revoke allocations
- ✅ ERC20 allocation tokens – Streams mint transferable ERC20 "vesting shares," making allocations composable with other DeFi protocols
- ✅ Minimal Proxy Clones (EIP-1167) – Every new stream is a cheap clone deployed by the factory, minimizing gas and storage costs
The system consists of three core contracts:
- Entry point for deploying vesting contracts
- Uses minimal proxy clones (EIP-1167) for gas-efficient deployments
- Supports multiple streams per user (both ERC20 and native HYPE)
- Manages linear vesting schedules with optional cliffs
- Supports editable and cancelable streams
- Mints an ERC20 "allocation token" to represent the beneficiary's claimable allocation, making streams composable with other DeFi protocols
- Optimized for bulk vesting distributions
- Stores a Merkle root of vesting schedules, allowing beneficiaries to prove and claim allocations with a Merkle proof
- Shares the same features as TokenVesting (linear release, cliff, cancelation), but for many users at once
Token.sol
— Simple ERC-20 token for testing and local developmentTokenVesting.sol
— Single-beneficiary vesting contract with linear release and optional cliffTokenVestingMerkle.sol
— Merkle-root-based vesting/claim contract for bulk distributionsVestingFactory.sol
— Factory that deploys vesting contracts using clones
Token.s.sol
— Deploy the test tokenTokenVesting.s.sol
— Deploy and exercise aTokenVesting
instanceClaimVesting.s.sol
— Example claim flow demonstrationVestingFactory.s.sol
— Factory deployment for mainnet/testnetVestingFactoryLocal.s.sol
— Factory deployment for local demos
TokenVesting.t.sol
— Unit tests for single-beneficiary vestingTokenVestingMerkle.t.sol
— Tests for Merkle claims and edge cases
- Install Foundry (forge, cast, anvil)
- POSIX shell (bash), curl, and Git
-
Start a local node:
anvil
-
Build and test in a separate terminal:
forge build forge test
-
Deploy to local Anvil node:
# Replace <ANVIL_KEY> with a private key from Anvil output forge script script/Token.s.sol:TokenScript --rpc-url http://127.0.0.1:8545 --private-key <ANVIL_KEY> --broadcast forge script script/VestingFactoryLocal.s.sol:VestingFactoryLocalScript --rpc-url http://127.0.0.1:8545 --private-key <ANVIL_KEY> --broadcast
vestingFactory.createVesting(
tokenAddress, // ERC20 token address
beneficiary, // Beneficiary address
block.timestamp, // Start time
30 days, // Cliff period
365 days, // Total duration
1000 ether // Total allocation
);
vestingFactory.createVesting{value: 100 ether}(
address(0), // Native HYPE marker
beneficiary, // Beneficiary address
block.timestamp, // Start time
90 days, // Cliff period
730 days, // Total duration
100 ether // Total allocation
);
// Deploy merkle vesting contract
vestingFactory.createMerkleVesting(
tokenAddress,
merkleRoot,
block.timestamp,
90 days, // Cliff period
730 days // Total duration
);
// Beneficiary claims their allocation
tokenVestingMerkle.claim(beneficiary, allocation, proof);
Read token balance:
cast call <token_address> "balanceOf(address)" <address> --rpc-url http://127.0.0.1:8545
Send transaction (approve example):
cast send --from <from_address> --private-key <ANVIL_KEY> <token_address> "approve(address,uint256)" <spender_address> <amount> --rpc-url http://127.0.0.1:8545
Call vesting view method:
cast call <vesting_address> "getVestingSchedule(address)" <beneficiary> --rpc-url http://127.0.0.1:8545
Run all tests:
forge test
Run specific test file:
forge test --match-path test/TokenVesting.t.sol
Run tests matching pattern:
forge test --match-test "testClaim*"
Verbose test output:
forge test --match-test "<test_name>" -vv
- Configure your RPC provider (Alchemy, Infura, etc.)
- Ensure your deployer account is funded
- Run deployment scripts with verification (optional)
forge script script/VestingFactory.s.sol:VestingFactoryScript \
--rpc-url https://your-rpc-endpoint \
--private-key $DEPLOYER_PRIVATE_KEY \
--broadcast \
--verify
├── src/ # Solidity contracts
├── script/ # Foundry deployment and demo scripts
├── test/ # Solidity tests
├── broadcast/ # Forge script broadcast outputs
└── lib/ # External dependencies (OpenZeppelin, forge-std, etc.)
- Follow existing code style and patterns
- Add comprehensive tests for new features
- Run
forge test
locally before submitting - Keep deployment scripts idempotent
- Document any side effects or breaking changes
Script broadcast failures:
- Check
broadcast/
directory for partial outputs - Verify RPC node connectivity and transaction status
- Ensure deployer account has sufficient funds
Test failures:
- Run with verbose flags (
-v
,-vv
,-vvv
) for detailed output - Check for proper contract state setup in test fixtures
Deployed Contracts
- TokenVesting Implementation: https://testnet.purrsec.com/address/0x4F816B31BCC2a7a4bB191f7375E90B6Ec003E164
- TokenVestingMerkle Implementation: https://testnet.purrsec.com/address/0x61614b9504f778aA2421245965Cc1D6b7E7cE445
- VestingFactory: https://testnet.purrsec.com/address/0x98ebc6f1badda2bc28540992cdd793c27cbd2247
For additional examples and advanced usage patterns, refer to the scripts in the script/
directory.