A smart contract for automatic bill splitting with group management and fair settlement options, built for Arbitrum using Stylus.
Status: ✅ WORKING IMPLEMENTATION - This is a fully functional Stylus smart contract that compiles and implements the core bill splitting functionality.
- Group Management: Create named groups with multiple members and ERC-20 settlement tokens
- Bill Tracking: Add multiple bills with descriptions and amounts
- Approval System: Members must approve their participation and provide ERC-20 allowances
- Automatic Settlement: When someone pays a bill, automatically debit other members
- Roulette Mode: Fair random selection for who pays the entire bill
- Event Logging: Complete on-chain tracking of all actions
pub fn create_group(
name: String,
token: Address,
member_names: Vec<String>,
member_addresses: Vec<Address>,
bill_descriptions: Vec<String>,
bill_amounts: Vec<U256>,
roulette_mode: bool,
) -> Result<U256, BillSplitterError>Creates a new group with members and bills. Returns the group ID.
pub fn approve_group(group_id: U256) -> Result<(), BillSplitterError>Member approves their participation in the group. Checks that they have sufficient ERC-20 allowance for their share.
pub fn settle_bill(
group_id: U256,
bill_index: usize,
payer: Address,
) -> Result<(), BillSplitterError>Settles a specific bill by transferring each member's share to the payer.
pub fn start_roulette_settlement(
group_id: U256,
payer: Address,
) -> Result<U256, BillSplitterError>
pub fn fulfill_roulette_settlement(
request_id: U256,
randomness: U256,
) -> Result<(), BillSplitterError>For roulette mode groups, randomly selects one member to pay the entire bill total.
pub fn get_group_info(group_id: U256) -> Result<(String, Address, U256, bool, bool), BillSplitterError>Returns: (name, token, total_cost, finalized, roulette_mode)
pub fn get_member_share(group_id: U256) -> Result<U256, BillSplitterError>Returns the equal share amount each member needs to approve.
// Create group for dinner with 4 people
let group_id = contract.create_group(
"Dinner Group".to_string(),
usdc_token_address, // ERC-20 token for payments
vec!["Alice".to_string(), "Bob".to_string(), "Charlie".to_string(), "Dave".to_string()],
vec![alice_addr, bob_addr, charlie_addr, dave_addr],
vec!["Restaurant".to_string(), "Drinks".to_string(), "Tip".to_string()],
vec![U256::from(2000), U256::from(500), U256::from(300)], // Total: 2800
false // Not roulette mode
)?;// Each member needs to approve allowance of 700 (2800/4) to the contract first
// Then call approve_group
contract.approve_group(group_id)?;// Alice pays the restaurant bill (2000), others automatically pay her their share (500 each)
contract.settle_bill(group_id, 0, alice_addr)?;
// Bob pays for drinks (500), others pay him their share (125 each)
contract.settle_bill(group_id, 1, bob_addr)?;The contract emits these events for UI tracking:
GroupCreated(group_id, name, token, creator)MemberApproved(group_id, member)GroupFinalized(group_id)- when all members approveBillAdded(group_id, bill_id, amount, description)BillSettled(group_id, bill_id, payer)RouletteSettled(group_id, selected, payer)TransferFailed(group_id, from, to, amount)- when ERC-20 transfer fails
- Rust toolchain
- Stylus CLI (
cargo install cargo-stylus)
cargo build --releasecargo stylus deploy --endpoint <arbitrum-rpc-url> --private-key <your-private-key>- Allowance Management: Users must maintain sufficient ERC-20 allowance and balance
- Failed Transfers: Contract handles failed transfers gracefully with events
- Reentrancy: Uses checks-effects-interactions pattern
- Randomness: Production should use Chainlink VRF for roulette mode
- Access Control: Only group members can perform certain actions
The contract includes comprehensive error types:
InsufficientMembers,GroupNotFound,GroupAlreadyFinalizedInsufficientAllowance,BillAlreadySettledERC20CallFailed,RouletteTransferFailed
- Uses flattened storage structures for efficiency
- Minimal loops and optimized storage access
- Events for off-chain indexing to reduce on-chain reads
- Weighted member contributions (not equal splits)
- Multiple settlement tokens per group
- Integration with payment processors
- Dispute resolution mechanisms
- Partial payment support