๐ Congratulations on making it to Day 30!
Today we combine everything you've learned into a complete Solana-style state machine.
Escrows are fundamental in DeFi. Here's the state machine:
Uninitialized โ Initialized โ Deposited โ Completed
โ โ
โโโโโโโโ Cancelled โโโโโโโโโThis pattern is used in token swaps, NFT marketplaces, governance proposals, and more!
| Current State | Action | Next State | Who Can Do It |
|---|---|---|---|
| Initialized | deposit | Deposited | Maker only |
| Deposited | complete | Completed | Anyone (taker) |
| Initialized/Deposited | cancel | Cancelled | Maker only |
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
enum EscrowState {
Uninitialized = 0,
Initialized = 1,
Deposited = 2,
Completed = 3,
Cancelled = 4,
}
#[derive(Debug)]
enum EscrowError {
InvalidState,
InvalidAmount,
Unauthorized,
}
#[repr(C)]
struct Escrow {
state: EscrowState,
maker: [u8; 32],
taker: [u8; 32],
amount: u64,
}Each transition should:
fn deposit(&mut self, caller: &[u8; 32], amount: u64) -> Result<(), EscrowError> {
// 1. Check state
if self.state != EscrowState::Initialized {
return Err(EscrowError::InvalidState);
}
// 2. Check authorization
if *caller != self.maker {
return Err(EscrowError::Unauthorized);
}
// 3. Validate input
if amount == 0 {
return Err(EscrowError::InvalidAmount);
}
// 4. Update
self.amount = amount;
self.state = EscrowState::Deposited;
Ok(())
}Implement the escrow state machine:
deposit(caller, amount) - maker deposits fundscomplete(taker) - someone completes the escrowcancel(caller) - maker cancels (if not completed)EscrowError on failurefn deposit(&mut self, caller: &[u8; 32], amount: u64) -> Result<(), EscrowError> {
if self.state != EscrowState::Initialized {
return Err(EscrowError::InvalidState);
}
if *caller != self.maker {
return Err(EscrowError::Unauthorized);
}
self.amount = amount;
self.state = EscrowState::Deposited;
Ok(())
}
fn complete(&mut self, taker: [u8; 32]) -> Result<(), EscrowError> {
if self.state != EscrowState::Deposited {
return Err(EscrowError::InvalidState);
}
self.taker = taker;
self.state = EscrowState::Completed;
Ok(())
}
fn cancel(&mut self, caller: &[u8; 32]) -> Result<(), EscrowError> {
if self.state == EscrowState::Completed {
return Err(EscrowError::InvalidState);
}
if *caller != self.maker {
return Err(EscrowError::Unauthorized);
}
self.state = EscrowState::Cancelled;
Ok(())
}You're now ready for Solana development! Your next steps:
Congratulations on completing 30 Days of Rust! ๐ฆ๐