A decentralized finance protocol built on StarkNet that combines token distribution and payment streaming capabilities. Fundable enables both bulk token distributions and continuous, real-time token streaming with advanced management features.
Fundable Protocol provides two main functionalities:
-
Token Distribution: Efficiently distribute tokens to multiple recipients in either:
- Equal amounts across all recipients
- Custom weighted amounts per recipient
-
Payment Streaming: Create and manage token streams that automatically distribute tokens over time with features like:
- Linear token streaming
- Stream management (pause, restart, cancel)
- Partial withdrawals
- Stream status tracking
The protocol consists of two main components:
- Handles bulk token distributions
- Manages token allowances and transfers
- Emits distribution events
- Supports both equal and weighted distributions
- Manages stream creation and lifecycle
- Handles withdrawals and stream modifications
- Tracks stream states and balances
- Provides stream query functions
- Scarb v2.8.5
- StarkNet Foundry v0.31.0
- Cairo v2.8.3
- Clone the repository
git clone https://github.com/fundable-protocol/fundable.git
cd fundable- Install dependencies
scarb buildRun the test suite:
snforge testFor verbose output:
scarb test -vuse fundable::distributor::Distributor;
// Create a distribution with equal amounts
let recipients = array![addr1, addr2, addr3];
distributor.distribute_equal(token, total_amount, recipients);
// Create a weighted distribution
let amounts = array![100, 200, 300];
distributor.distribute_weighted(token, recipients, amounts);use fundable::payment_stream::PaymentStream;
// Create a new stream
let stream = payment_stream.create_stream(
recipient,
total_amount,
start_time,
end_time,
token,
transferable
);
// Withdraw from stream
payment_stream.withdraw(stream_id, amount);
// Pause stream
payment_stream.pause_stream(stream_id);We welcome contributions! Please follow these steps:
- Fork the repository
- Create a new branch (
git checkout -b feature/amazing-feature) - Make your changes and write tests to ensure your changes are working as expected
- Run tests (
snforge test) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
We use Conventional Commits. Examples:
feat: add new streaming featurefix: handle edge case in calculationdocs: update installation instructionstest: add test for edge casechore: update dependencies
- Use
snake_casefor functions and variables - Use
PascalCasefor types and traits - Use
SCREAMING_SNAKE_CASEfor constants - Add documentation comments (
///) for public interfaces - Follow Cairo code style guidelines
For security concerns, please email security@fundable.com or open a draft security advisory on Github.
This project is licensed under the MIT License - see the LICENSE file for details.
- StarkWare for the Cairo programming language
- OpenZeppelin for security best practices and implementations
- The StarkNet community for their continuous support
- Telegram: Join our channel
- Twitter: @fundable
The PaymentStream contract emits the following events to track stream lifecycle and operations:
Emitted when a new payment stream is created via create_stream().
Event::StreamCreated(
StreamCreated {
stream_id, // Unique stream identifier
sender, // Stream creator address
recipient, // Payment recipient address
total_amount, // Total tokens to be streamed
token, // ERC20 token address
transferable // NFT Transferability Flag
}
)Emitted when tokens are withdrawn via withdraw() or withdraw_max().
Event::StreamWithdrawn(
StreamWithdrawn {
stream_id, // Stream identifier
recipient, // Address receiving the withdrawal
amount, // Amount withdrawn
protocol_fee // Fee charged by protocol
}
)Emitted when a stream is canceled by an authorized user via cancel().
Event::StreamCanceled(
StreamCanceled {
stream_id, // Stream identifier
remaining_balance // Tokens returned to sender
}
)Emitted when a stream is paused via pause_stream().
Event::StreamPaused(
StreamPaused {
stream_id, // Stream identifier
paused_at // Timestamp of pause
}
)Emitted when a paused stream is restarted via restart_stream().
Event::StreamRestarted(
StreamRestarted {
stream_id, // Stream identifier
restarted_at // Timestamp of restart
}
)Emitted when a stream is permanently voided via void_stream().
Event::StreamVoided(
StreamVoided {
stream_id, // Stream identifier
void_reason // Reason code for voiding
}
)stream_id:u256- Unique identifier for each streamsender:ContractAddress- Starknet address of stream creatorrecipient:ContractAddress- Starknet address of payment recipienttotal_amount:u256- Amount of tokens in streamtoken:ContractAddress- ERC20 token contract addressamount:u256- Amount of tokens in transactionprotocol_fee:u128- Protocol fee amountremaining_balance:u256- Remaining tokens in streampaused_at/restarted_at:u64- Unix timestampvoid_reason:u8- Numeric code indicating void reason