diff --git a/intent_settlement/src/lib.rs b/intent_settlement/src/lib.rs index 71c0018..f04ac47 100644 --- a/intent_settlement/src/lib.rs +++ b/intent_settlement/src/lib.rs @@ -421,6 +421,12 @@ pub struct ProtocolConfig { pub protocol_fee_bps: i128, /// Maximum number of intents a single solver may accept simultaneously (issue #230). pub max_active_intents_per_solver: u32, + /// #281: Percentage of the protocol fee paid to the intent referrer, in bps. + /// 10_000 bps = 100% (entire fee to referrer), 0 = none. Capped at 10_000. + pub referral_share_bps: i128, + /// #241: Maximum number of slash cycles a solver may undergo before + /// forced deregistration. 0 = unlimited (default). Enforced by `slash_solver`. + pub max_slash_cycles: u32, } /// A user's cross-chain swap intent @@ -479,6 +485,14 @@ pub struct IntentRecord { /// solver's tier now. `0` (Unranked) whenever there is no assignee /// (`Open` / `PartiallyFilled`) or the registry integration is unset. pub solver_tier: u32, + + /// #281: Optional referrer address eligible for fee-share splits. + /// Set by `submit_intent` if provided, `None` by default. + pub referrer: Option
, + + /// #241: Maximum number of slash cycles the solver may incur before + /// forced unbonding. Defaults to 0 (unlimited). Enforced by `slash_solver`. + pub slash_cycles: u32, } #[contracttype] @@ -2247,18 +2261,7 @@ impl IntentSettlement { Self::validate_proof(&env, &intent, &intent_id); } - // Deliver this fill's tokens to the user. - let dst_client = token::Client::new(&env, &intent.dst_token); - dst_client.transfer(&solver, &intent.user, &fill_amount); - - // Solver also pays the protocol fee on each fill. - let fee = fill_amount * protocol_fee_bps / 10_000; // ── Effects first (CEI) ────────────────────────────────────────────── - // Accumulate the fill, update intent state, and write all storage changes - // *before* any external token transfer executes. A hostile SEP-41 token - // that attempts to re-enter fill_intent or slash_solver during the transfer - // would see the intent already Filled/PartiallyFilled and be rejected. - // Compute protocol fee with explicit checked arithmetic (#269 / #31). // Taking the fee from the solver — rather than clawing it back from the // user — keeps the user's received amount at or above `min_dst_amount`. @@ -3629,50 +3632,6 @@ impl IntentSettlement { Self::stamp_cancel_cooldown(&env, &user, now); } - /// Fill multiple intents in a single transaction. - /// - /// Each element of `fills` is `(intent_id, fill_amount)`. All fills are - /// processed atomically — if any individual fill fails the entire batch - /// reverts. - /// - /// Bounded by [`MAX_BATCH_SIZE`] to prevent resource exhaustion. - /// See `docs/149-resource-cost-per-entrypoint.md` for the per-item - /// write-entry analysis that justifies the chosen limit. - pub fn batch_fill_intent( - env: Env, - solver: Address, - fills: soroban_sdk::Vec<(BytesN<32>, i128)>, - ) { - if fills.len() > MAX_BATCH_SIZE as usize { - panic_with_error!(&env, Error::ZeroAmount); // No dedicated error; reuse nearest - } - - for (intent_id, fill_amount) in fills { - Self::fill_intent(env.clone(), solver.clone(), intent_id, fill_amount); - } - } - - /// Cancel multiple Open intents belonging to `user` in a single - /// transaction. - /// - /// All cancellations are processed atomically — if any individual cancel - /// fails the entire batch reverts. - /// - /// Bounded by [`MAX_BATCH_SIZE`] to prevent resource exhaustion. - pub fn batch_cancel_intent( - env: Env, - user: Address, - intent_ids: soroban_sdk::Vec