agents/skills/sui/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|
balance::join|balance::split|coin::mint|reward_index|cumulative|epoch.*reward
Analyze fairness of share/token allocation mechanisms on Sui where users receive Coin<T> shares or Balance<T> proportional to deposits, contributions, or participation -- checking for late-entry advantages, PTB-based timing exploitation, queue-position gaming, and time-weighting omissions.
Sui-specific share representations:
Coin<ShareToken>: Fungible, freely transferable (has key + store). User holds as owned object.Balance<ShareToken> inside a position object: Non-transferable accounting. Locked within the protocol's object model.u64 field in a shared/owned struct: Simple numerical tracking, no token representation.Identify which pattern the protocol uses:
| Type | Sui Pattern | Key Risk |
|------|------------|----------|
| Pro-rata snapshot | Shares minted at fixed ratio via balance::split/coin::mint_balance at deposit time | Late depositors dilute early depositors' accrued value |
| Time-weighted | Per-user owned object tracks reward_per_share_paid and accrued_rewards with clock::timestamp_ms() | Checkpoint manipulation, discrete vs continuous accrual |
| Queue-based | Table/VecMap in shared object stores pending deposits | Queue position gaming, PTB-based front-running |
| Epoch-based | Shares valued per Sui epoch boundary via tx_context::epoch() | Cross-epoch timing arbitrage at epoch transition |
For each allocation entry function:
| Entry Function | Accrual Source | Time-Weighted? | Late Entry Possible? | Impact | |---------------|----------------|----------------|---------------------|--------|
Sui timing model:
clock::timestamp_ms() provides millisecond-precision timestamps (read from the Clock shared object)clock::timestamp_ms() have ~0.5-2s granularity. An attacker can deposit and withdraw within the same checkpoint and see zero time elapsed, potentially capturing rewards with zero time commitment.PTB-specific timing: With PTB composability, an attacker can compose multiple function calls in a single atomic transaction: [deposit] -> [trigger_distribution] -> [withdraw] within one PTB. This is more powerful than EVM flash loans for timing attacks because PTBs execute atomically with no inter-step cost.
For each entry function accepting a recipient address parameter:
| Entry Function | Accepts Recipient? | Default State for New Recipient | Exploitable? | Impact | |---------------|-------------------|-------------------------------|-------------|--------|
Check: When a new position object or dynamic field is created for a recipient:
reward_per_share_paid = 0? last_deposit_epoch = 0?)reward_per_share_paid starts at 0 while the global index is at N, the new position holder captures ALL historical rewards on their deposit -- FINDINGdeposit(recipient, coin) where recipient != sender create a position that captures historical rewards the recipient did not earn?For each admin-settable reward/rate parameter:
| Parameter Setter | Cap Required | Staked-Before-Set? | Retroactive Rewards? | Fair? | |-----------------|-------------|-------------------|---------------------|-------|
Model: user deposits (position created with current index) -> admin sets reward rate -> rewards accrue.
For the allocation mechanism identified in Step 1:
| Configuration Step | Parameter Set | Functions Available Before Set | Exploitable Default? | |--------------------|-------------|-------------------------------|---------------------|
init runs once at package publish. What configuration happens in init vs subsequent admin transactions?is_initialized check that gates user interactions?Sui-specific: init() runs atomically at publish. If configuration requires MULTIPLE transactions (init -> configure_pool -> set_rates), there are windows between these transactions where the protocol is partially configured.
If users can interact during partial configuration AND default values create unfair advantage -> FINDING (minimum Medium, Rule 13: design gap).
For protocols with batch/queue processing:
Coin<T> into many small deposits (via coin::split in a PTB) for queue advantage or per-deposit limit bypass?Sui-specific ordering:
Check that entry and exit use consistent valuation:
Sui-specific redemption:
Coin<ShareToken>, user burns them via protocol function. Check: can user transfer shares to another address and redeem there to bypass cooldowns?Balance<ShareToken> inside position object, redemption requires the position object. Check: can position object be transferred (has store?) to bypass restrictions?total_supply == 0 and someone deposits? (division by zero in share calculation?)TreasuryCap authority risks:
store allows extraction from the wrapper -> unauthorized minting.For independently-settable allocation rates/shares (e.g., per-pool weights, fee splits, distribution percentages):
| Rate/Weight Setter | Aggregate Constraint | Enforced On-Chain? | What if Sum Exceeds/Falls Short? | |-------------------|---------------------|-------------------|--------------------------------|
Sui-specific: If weights are stored as dynamic fields on a shared object (one field per pool), the setter function may not iterate all fields to validate the sum. Check: does the setter read all weight dynamic fields and validate the total?
If aggregate constraint NOT enforced and rates independently settable -> FINDING (Rule 14).
For each finding, specify:
clock::timestamp_ms() granularity, checkpoint ordering)**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**: sources/{module}.move:LineN
**Title**: {fairness violation type}
**Description**: {specific issue with numerical example}
**Impact**: {quantified at worst-state parameters -- who loses how much}
| Step | Required | Completed? | Notes | |------|----------|------------|-------| | 1. Classify Allocation Mechanism | YES | | | | 2. Late Entry Attack Model | YES | | PTB composition timing check | | 2c. Cross-Address Deposit Model | YES | | Check recipient != sender patterns | | 2d. Pre-Setter Timing Model | YES | | Model deposit-before-rate-set sequence | | 2e. Pre-Configuration State Analysis | YES | | Post-init() window + unconfigured defaults | | 3. Queue Position and Batch Processing | IF queue/batch detected | | Include PTB deposit splitting | | 4. Share Redemption Symmetry | YES | | Include TreasuryCap access 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