|
| 1 | +# Bidding Documentation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document describes the bidding mechanism in the QuickLendX protocol, focusing on bid validation rules and protocol minimum enforcement as implemented in issue #719. |
| 6 | + |
| 7 | +## Bid Validation |
| 8 | + |
| 9 | +### Bid Structure |
| 10 | + |
| 11 | +A bid in QuickLendX contains the following key components: |
| 12 | + |
| 13 | +```rust |
| 14 | +pub struct Bid { |
| 15 | + pub bid_id: BytesN<32>, // Unique identifier for the bid |
| 16 | + pub invoice_id: BytesN<32>, // Associated invoice |
| 17 | + pub investor: Address, // Investor address |
| 18 | + pub bid_amount: i128, // Amount being bid |
| 19 | + pub expected_return: i128, // Expected return for investor |
| 20 | + pub timestamp: u64, // Bid creation time |
| 21 | + pub expiration_timestamp: u64, // Bid expiration time |
| 22 | + pub status: BidStatus, // Current bid status |
| 23 | +} |
| 24 | +``` |
| 25 | + |
| 26 | +### Bid Status Lifecycle |
| 27 | + |
| 28 | +```rust |
| 29 | +pub enum BidStatus { |
| 30 | + Placed, // Bid is active and can be accepted |
| 31 | + Withdrawn, // Bid was withdrawn by investor |
| 32 | + Accepted, // Bid was accepted and investment created |
| 33 | + Expired, // Bid expired without acceptance |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +## Protocol Minimum Enforcement |
| 38 | + |
| 39 | +### Minimum Bid Calculation |
| 40 | + |
| 41 | +The protocol enforces a minimum bid amount using both absolute and percentage-based constraints: |
| 42 | + |
| 43 | +```rust |
| 44 | +let percent_min = invoice.amount |
| 45 | + .saturating_mul(limits.min_bid_bps as i128) |
| 46 | + .saturating_div(10_000); |
| 47 | + |
| 48 | +let effective_min_bid = if percent_min > limits.min_bid_amount { |
| 49 | + percent_min // Use percentage minimum if higher |
| 50 | +} else { |
| 51 | + limits.min_bid_amount // Use absolute minimum otherwise |
| 52 | +}; |
| 53 | +``` |
| 54 | + |
| 55 | +### Protocol Limits |
| 56 | + |
| 57 | +The protocol maintains configurable minimum bid parameters: |
| 58 | + |
| 59 | +```rust |
| 60 | +pub struct ProtocolLimits { |
| 61 | + pub min_bid_amount: i128, // Absolute minimum bid amount |
| 62 | + pub min_bid_bps: u32, // Minimum bid as percentage (basis points) |
| 63 | + // ... other limits |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +**Default Values:** |
| 68 | +- `min_bid_amount`: 10 (smallest unit) |
| 69 | +- `min_bid_bps`: 100 (1% of invoice amount) |
| 70 | + |
| 71 | +### Validation Rules |
| 72 | + |
| 73 | +#### 1. Amount Validation |
| 74 | +- **Non-zero**: Bid amount must be > 0 |
| 75 | +- **Minimum Enforcement**: Must meet or exceed `effective_min_bid` |
| 76 | +- **Invoice Cap**: Cannot exceed total invoice amount |
| 77 | + |
| 78 | +#### 2. Invoice Status Validation |
| 79 | +- **Verified Only**: Invoice must be in `Verified` status |
| 80 | +- **Not Expired**: Invoice due date must be in the future |
| 81 | + |
| 82 | +#### 3. Ownership Validation |
| 83 | +- **No Self-Bidding**: Business cannot bid on own invoices |
| 84 | +- **Authorization**: Only verified investors can place bids |
| 85 | + |
| 86 | +#### 4. Investor Capacity |
| 87 | +- **Investment Limits**: Total active bids cannot exceed investor's verified limit |
| 88 | +- **Risk Assessment**: Bid amount considered against investor's risk profile |
| 89 | + |
| 90 | +#### 5. Bid Protection |
| 91 | +- **Single Active Bid**: One investor cannot have multiple active bids on same invoice |
| 92 | +- **Expiration Handling**: Expired bids are automatically cleaned up |
| 93 | + |
| 94 | +## Bid Placement Flow |
| 95 | + |
| 96 | +### 1. Pre-Validation |
| 97 | +```rust |
| 98 | +validate_bid(env, invoice, bid_amount, expected_return, investor)?; |
| 99 | +``` |
| 100 | + |
| 101 | +### 2. Bid Creation |
| 102 | +```rust |
| 103 | +let bid_id = BidStorage::generate_unique_bid_id(env); |
| 104 | +let bid = Bid { |
| 105 | + bid_id, |
| 106 | + invoice_id, |
| 107 | + investor, |
| 108 | + bid_amount, |
| 109 | + expected_return, |
| 110 | + timestamp: env.ledger().timestamp(), |
| 111 | + expiration_timestamp: env.ledger().timestamp() + bid_ttl_seconds, |
| 112 | + status: BidStatus::Placed, |
| 113 | +}; |
| 114 | +``` |
| 115 | + |
| 116 | +### 3. Storage |
| 117 | +```rust |
| 118 | +BidStorage::store_bid(env, &bid); |
| 119 | +BidStorage::add_bid_to_invoice_index(env, &invoice_id, &bid_id); |
| 120 | +BidStorage::add_bid_to_investor_index(env, &investor, &bid_id); |
| 121 | +``` |
| 122 | + |
| 123 | +## Bid Selection and Ranking |
| 124 | + |
| 125 | +### Best Bid Selection |
| 126 | +The protocol selects the best bid based on: |
| 127 | + |
| 128 | +1. **Profit Priority**: Higher expected return (profit = expected_return - bid_amount) |
| 129 | +2. **Return Amount**: Higher expected return if profit equal |
| 130 | +3. **Bid Amount**: Higher bid amount if profit and return equal |
| 131 | +4. **Timestamp**: Newer bids preferred (deterministic tiebreaker) |
| 132 | +5. **Bid ID**: Final stable tiebreaker |
| 133 | + |
| 134 | +### Ranking Algorithm |
| 135 | +```rust |
| 136 | +pub fn compare_bids(bid1: &Bid, bid2: &Bid) -> Ordering { |
| 137 | + let profit1 = bid1.expected_return.saturating_sub(bid1.bid_amount); |
| 138 | + let profit2 = bid2.expected_return.saturating_sub(bid2.bid_amount); |
| 139 | + |
| 140 | + // Compare by profit, then return, then amount, then timestamp, then bid_id |
| 141 | + // This ensures reproducible ranking across all validators |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +## Security Considerations |
| 146 | + |
| 147 | +### Reentrancy Protection |
| 148 | +- All bid modifications require proper authorization |
| 149 | +- State transitions are atomic |
| 150 | +- External calls minimized during validation |
| 151 | + |
| 152 | +### Access Control |
| 153 | +- **Investor Authorization**: `investor.require_auth()` for bid placement |
| 154 | +- **Business Authorization**: `business.require_auth()` for invoice operations |
| 155 | +- **Admin Authorization**: `AdminStorage::require_admin()` for protocol changes |
| 156 | + |
| 157 | +### Input Validation |
| 158 | +- **Amount Bounds**: Prevents overflow and underflow |
| 159 | +- **Timestamp Validation**: Ensures logical time progression |
| 160 | +- **Address Validation**: Prevents invalid address usage |
| 161 | + |
| 162 | +## Gas Optimization |
| 163 | + |
| 164 | +### Efficient Storage |
| 165 | +- **Indexed Storage**: Fast lookup by invoice, investor, and status |
| 166 | +- **Batch Operations**: Multiple bids processed in single transaction |
| 167 | +- **Cleanup Routines**: Automatic removal of expired bids |
| 168 | + |
| 169 | +### Minimal Computations |
| 170 | +- **Saturating Arithmetic**: Prevents overflow without expensive checks |
| 171 | +- **Lazy Evaluation**: Calculations deferred until needed |
| 172 | +- **Constants**: Pre-computed values where possible |
| 173 | + |
| 174 | +## Testing Coverage |
| 175 | + |
| 176 | +### Unit Tests |
| 177 | +- **Validation Logic**: All validation rules tested |
| 178 | +- **Edge Cases**: Boundary conditions and error scenarios |
| 179 | +- **Protocol Limits**: Custom limit configurations tested |
| 180 | +- **Integration Tests**: End-to-end bid placement flows |
| 181 | + |
| 182 | +### Test Coverage Requirements |
| 183 | +- **95% Coverage**: All bid validation paths tested |
| 184 | +- **Error Paths**: All error conditions validated |
| 185 | +- **Success Paths**: All valid bid scenarios covered |
| 186 | +- **Edge Cases**: Boundary values and special conditions |
| 187 | + |
| 188 | +## Event Emission |
| 189 | + |
| 190 | +### Bid Events |
| 191 | +```rust |
| 192 | +// Events emitted during bid lifecycle |
| 193 | +emit_bid_placed(env, &bid_id, &invoice_id, &investor, bid_amount); |
| 194 | +emit_bid_accepted(env, &bid_id, &invoice_id, &investor, bid_amount); |
| 195 | +emit_bid_withdrawn(env, &bid_id, &invoice_id, &investor, bid_amount); |
| 196 | +emit_bid_expired(env, &bid_id, &invoice_id, &investor, bid_amount); |
| 197 | +``` |
| 198 | + |
| 199 | +### Audit Trail |
| 200 | +- All bid state transitions are logged |
| 201 | +- Timestamps recorded for all operations |
| 202 | +- Authorization verified for all state changes |
| 203 | + |
| 204 | +## Configuration |
| 205 | + |
| 206 | +### Bid TTL (Time-To-Live) |
| 207 | +- **Default**: 7 days from placement |
| 208 | +- **Configuration**: Set by admin via `set_bid_ttl` |
| 209 | +- **Cleanup**: Automatic expiration and status updates |
| 210 | + |
| 211 | +### Maximum Active Bids |
| 212 | +- **Default**: 10 per investor |
| 213 | +- **Purpose**: Prevents spam and manages risk |
| 214 | +- **Enforcement**: Checked during bid placement |
| 215 | + |
| 216 | +## Migration Notes |
| 217 | + |
| 218 | +### Backward Compatibility |
| 219 | +- Existing bids remain valid under previous rules |
| 220 | +- New protocol limits apply to future bids |
| 221 | +- Storage format unchanged for existing data |
| 222 | + |
| 223 | +### Upgrade Path |
| 224 | +- Protocol limits can be updated by admin |
| 225 | +- Bid validation logic can be enhanced without breaking changes |
| 226 | +- New bid statuses can be added via enum extension |
| 227 | + |
| 228 | +## Best Practices |
| 229 | + |
| 230 | +### For Investors |
| 231 | +- **Due Diligence**: Verify invoice details before bidding |
| 232 | +- **Risk Management**: Don't exceed investment capacity |
| 233 | +- **Timing**: Place bids well before expiration |
| 234 | +- **Monitoring**: Track active bids and their status |
| 235 | + |
| 236 | +### For Businesses |
| 237 | +- **Verification**: Ensure invoice is verified before expecting bids |
| 238 | +- **Terms**: Clear payment terms and due dates |
| 239 | +- **Communication**: Respond to appropriate bids promptly |
| 240 | + |
| 241 | +### For Protocol Developers |
| 242 | +- **Validation**: Centralize bid validation logic |
| 243 | +- **Testing**: Comprehensive test coverage for all scenarios |
| 244 | +- **Documentation**: Clear NatSpec comments for all functions |
| 245 | +- **Security**: Regular security audits of bid logic |
0 commit comments