Automated payroll splitting for teams and DAOs on Base
SplitStream is a smart contract protocol that enables automatic payment distribution among multiple payees based on predefined share allocations. Built on Base, it provides a trustless and transparent way to split incoming ETH payments proportionally.
- Proportional Payment Distribution: Automatically splits ETH among payees based on their share allocation
- Pull Payment Pattern: Payees withdraw their funds when ready, reducing gas costs and improving security
- Immutable Share Allocation: Shares are set at deployment and cannot be changed, ensuring predictability
- Event Logging: All payments and releases are logged on-chain for transparency
- Gas Efficient: Optimized for minimal gas consumption using OpenZeppelin utilities
- Fully Tested: Comprehensive test suite with 100% coverage of core functionality
The main contract that handles payment splitting and distribution.
Key Functions:
totalShares(): Returns the total number of shares across all payeesshares(address payee): Returns the number of shares for a specific payeereleased(address payee): Returns the amount already released to a payeetotalReleased(): Returns the total amount released to all payeespayee(uint256 index): Returns the payee address at a given indexrelease(address payable account): Releases the owed payment to a payee
Events:
PaymentReceived(address indexed from, uint256 amount): Emitted when ETH is receivedPaymentReleased(address indexed to, uint256 amount): Emitted when a payee withdraws
# Clone the repository
git clone https://github.com/Yusufolosun/splitstream-protocol.git
cd splitstream-protocol
# Install dependencies
npm install
# Copy environment template
cp .env.example .envEdit .env file with your credentials:
BASE_RPC_URL=https://mainnet.base.org
PRIVATE_KEY=your_private_key_here
BASESCAN_API_KEY=your_basescan_api_key_hereRun the complete test suite:
npx hardhat testExpected output:
SplitStream
Deployment
✔ Should set the correct total shares
✔ Should assign shares correctly to each payee
Payment Release
✔ Should allow payees to release their shares
✔ Should prevent double payments
Receive Function
✔ Should receive ETH and emit events
18 passing (2s)
npx hardhat run scripts/deploy.js --network baseThe deployment script will:
- Deploy the SplitStream contract with configured payees and shares
- Wait for block confirmation
- Verify the deployment on-chain
- Provide the contract address and verification command
# Start local Hardhat node
npx hardhat node
# In another terminal, deploy
npx hardhat run scripts/deploy.js --network localhostAfter deployment, verify your contract on Basescan:
npx hardhat verify --network base <CONTRACT_ADDRESS> "[\"<PAYEE1>\",\"<PAYEE2>\",\"<PAYEE3>\"]" "[50,30,20]"Example:
npx hardhat verify --network base 0x1234... "[\"0xABC...\",\"0xDEF...\",\"0x123...\"]" "[50,30,20]"const SplitStream = await ethers.getContractFactory("SplitStream");
const payees = [
"0x1111111111111111111111111111111111111111",
"0x2222222222222222222222222222222222222222",
"0x3333333333333333333333333333333333333333"
];
const shares = [50, 30, 20]; // 50%, 30%, 20%
const splitter = await SplitStream.deploy(payees, shares);
await splitter.waitForDeployment();// Anyone can send ETH to the contract
await signer.sendTransaction({
to: contractAddress,
value: ethers.parseEther("10.0") // Send 10 ETH
});// Any payee can call release for themselves or others
await splitter.release(payeeAddress);
// Check how much has been released
const released = await splitter.released(payeeAddress);
console.log(`Released: ${ethers.formatEther(released)} ETH`);// Get total shares
const totalShares = await splitter.totalShares();
// Get shares for a specific payee
const payeeShares = await splitter.shares(payeeAddress);
// Get total amount released
const totalReleased = await splitter.totalReleased();
// Get payee by index
const payee0 = await splitter.payee(0);┌─────────────────────────────────────────┐
│ External Accounts │
│ (Send ETH to SplitStream) │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ SplitStream Contract │
│ ┌───────────────────────────────────┐ │
│ │ Receive ETH │ │
│ │ - Emit PaymentReceived event │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Calculate Shares │ │
│ │ - Based on total shares │ │
│ │ - Proportional distribution │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Release Payments │ │
│ │ - Pull payment pattern │ │
│ │ - Track released amounts │ │
│ │ - Emit PaymentReleased event │ │
│ └───────────────────────────────────┘ │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Payees │
│ (Withdraw their proportional share) │
└─────────────────────────────────────────┘
- Immutable Shares: Share allocations are set at deployment and cannot be modified
- Pull Payment Pattern: Payees must actively withdraw funds, preventing push payment vulnerabilities
- Reentrancy Protection: Uses OpenZeppelin's
sendValuewhich includes reentrancy protection - Zero Address Checks: Validates all payee addresses during deployment
- Zero Shares Prevention: Ensures all payees have non-zero share allocations
- Duplicate Prevention: Prevents the same address from being added multiple times
- Uses
uint256for efficient storage and computation - Minimal storage reads through caching
- Efficient iteration patterns
- OpenZeppelin's optimized utilities
- Solidity: ^0.8.20
- Hardhat: Development environment
- OpenZeppelin Contracts: Secure, audited utilities
- Ethers.js: Ethereum library
- Chai: Testing assertions
- Base: Deployment network
splitstream-protocol/
├── contracts/
│ ├── SplitStream.sol # Main payment splitter contract
│ └── ISplitStream.sol # Interface definition
├── scripts/
│ └── deploy.js # Deployment script
├── test/
│ └── SplitStream.test.js # Comprehensive test suite
├── hardhat.config.js # Hardhat configuration
├── .env.example # Environment template
└── README.md # This file
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For questions or issues:
- Open an issue on GitHub
- Check existing documentation and tests
- Built with OpenZeppelin Contracts
- Deployed on Base
- Inspired by payment splitter patterns in DeFi
⚡ Built on Base | 🔒 Trustless | 📊 Transparent