agents/skills/soroban/share-allocation-fairness/SKILL.md
Trigger Pattern SHARE_ALLOCATION flag detected in pattern scan - Inject Into Breadth agents, depth-edge-case
npx skillsauth add plamentsv/plamen share-allocation-fairnessInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Trigger Pattern: SHARE_ALLOCATION flag detected in pattern scan Inject Into: Breadth agents, depth-edge-case Finding prefix:
[SAF-N]Rules referenced: R5, R10, R13, R14
shares|allocation|distribute|pro_rata|proportional|vest|reward_per_share|
mint|burn|reward_index|cumulative|checkpoint|epoch_reward
Analyze fairness of share/token allocation mechanisms on Soroban where users receive shares proportional to deposits, contributions, or participation — checking for late-entry advantages, storage-default manipulation, authorization bypass, and time-weighting omissions.
Soroban arithmetic constraint: ALL share math uses i128 (signed 128-bit integer). There is no floating-point arithmetic. All pro-rata and time-weighted calculations must use multiply-then-divide integer patterns. Division truncates toward zero. Overflow with unchecked * on i128 wraps silently in Rust release mode — use checked_mul.
Identify which pattern the protocol uses:
| Type | Soroban Pattern | Key Risk |
|------|----------------|----------|
| Pro-rata snapshot | Shares minted at fixed ratio via mint() on custom SEP-41 token at deposit time | Late depositors dilute early depositors' accrued value |
| Time-weighted | Per-user Persistent entry tracks reward_per_share_paid: i128 and accrued_rewards: i128 | Checkpoint manipulation, stale reward index |
| Epoch-based | Shares valued per ledger-sequence epoch; epoch tracked in Instance storage | Cross-epoch timing arbitrage at epoch transition ledgers |
| Queue-based | Ordered list in Instance/Persistent storage (Vec or linked entries) | Queue position gaming, partial processing fairness |
For each allocation entry function:
| Entry Function | Accrual Source | Time-Weighted? | Late Entry Possible? | Impact | |---------------|----------------|----------------|---------------------|--------|
Soroban timing: With ~5s ledger close time, timing attacks require multi-ledger execution. However, in high-value situations an attacker can monitor on-chain state and submit a transaction to land in the specific ledger just before a known distribution event.
For each entry function accepting a recipient: Address parameter that is NOT verified to be the function caller:
| Entry Function | Accepts Recipient != Caller? | Default State for New Persistent Entry | Exploitable? | Impact | |---------------|------------------------------|---------------------------------------|-------------|--------|
Check: When a new Persistent entry is created for a recipient address:
reward_per_share_paid: i128 = 0? last_deposit_ledger: u32 = 0?)reward_per_share_paid starts at 0 while the global index is at N, the new entry holder is entitled to ALL historical rewards on their deposit — FINDING (late-entry variant)deposit(recipient, amount) where recipient != caller be used to create a new Persistent entry that captures historical rewards the recipient did not earn?For each admin-settable reward/rate parameter stored in Instance storage:
| Parameter Setter | Staked-Before-Set? | Retroactive Rewards? | Fair? | |-----------------|-------------------|---------------------|-------|
Model the sequence: user deposits (Persistent entry created with current index) → admin sets reward rate → rewards accrue.
For the allocation mechanism identified in Step 1:
| Configuration Step | Storage Key Initialized | Functions Available Before Init | Exploitable Default? | |-------------------|------------------------|--------------------------------|---------------------|
initialize_* functions in order.None)?None → unwrap_or(0) defaults?is_initialized flag in Instance storage or an admin check that prevents interaction before configuration completes?If users can interact during partial initialization AND default storage values create unfair advantage → FINDING (minimum Medium, Rule 13: design gap).
For protocols with queue-based or batch processing:
Resource-aware batching: If batch processing iterates over a Vec stored in Instance storage:
Check that entry and exit use consistent valuation:
i128 math: does rounding in deposit vs withdraw consistently favor one party?floor(amount * total_shares / total_value) shares; withdraw gives floor(shares * total_value / total_shares) tokens — rounding may always favor the protocolToken admin risks in Soroban:
For independently-settable allocation rates/shares (e.g., per-pool weights, fee splits, distribution percentages stored as separate Instance storage keys):
| Rate/Weight Setter | Aggregate Constraint | Enforced On-Chain? | What if Sum Exceeds/Falls Short? | |-------------------|---------------------|-------------------|--------------------------------|
Soroban-specific: If weights are stored as separate Instance storage keys (one per pool), the setter function may update ONE key without reading and summing ALL keys to validate the aggregate. This is especially problematic when each setter only receives its own key as context.
If aggregate constraint NOT enforced and rates independently settable → FINDING (Rule 14).
Also check: Can admin set a weight to 0 for an active pool? What happens to users with deposits in that pool? (Rule 14 setter regression — setting weight below accumulated state causes division-by-zero or zero-allocation for existing depositors)
For each finding, specify:
i128 example (token amounts in smallest unit, e.g., stroops for XLM)**ID**: [SAF-N]
**Verdict**: CONFIRMED / PARTIAL / REFUTED / CONTESTED
**Step Execution**: (see checklist below)
**Rules Applied**: [R5:___, R10:___, R13:___, R14:___]
**Severity**: Critical/High/Medium/Low/Info
**Location**: src/{file}.rs:LineN
**Title**: {fairness violation type}
**Description**: {specific issue with i128 numerical example}
**Impact**: {quantified at worst-state parameters — who loses how much, in stroops or token units}
| Step | Required | Completed? | Notes | |------|----------|------------|-------| | 1. Classify Allocation Mechanism | YES | | | | 2. Late Entry Attack Model | YES | | | | 2c. Cross-Address Deposit Model | YES | | Check recipient != caller patterns | | 2d. Pre-Setter Timing Model | YES | | Model deposit-before-rate-set sequence | | 2e. Pre-Configuration State Analysis | YES | | Deployment window + uninitialized storage defaults | | 3. Queue Position and Batch Processing | IF queue/batch detected | | Include resource-aware batch analysis | | 4. Share Redemption Symmetry | YES | | Include custom token mint + SAC freeze check | | 4b. Aggregate Constraint Coherence | IF multiple settable weights | | Rule 14 enforcement check |
If any step skipped, document valid reason (N/A, no queue, single pool, no settable weights).
development
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline