Skip to content

Commit 8444595

Browse files
Merge pull request #504 from somotochukwu-dev/rebaseline-wasm-size-427
feat: rebaseline wasm size
2 parents f3de33a + e8d2fb9 commit 8444595

8 files changed

Lines changed: 25 additions & 269 deletions

File tree

.wasm-size-baseline

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
180000
1+
199953

contracts/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
crate-type = ["cdylib", "rlib"]
88

99
[dependencies]
10-
soroban-sdk = { workspace = true }
10+
soroban-sdk = { workspace = true, features = ["alloc"] }
1111

1212
[dev-dependencies]
1313
soroban-sdk = { workspace = true, features = ["testutils"] }

contracts/src/errors.rs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,28 +103,24 @@ pub enum ContractError {
103103
IllegalPhaseTransition = 84,
104104
/// Oracle heartbeat failed the configured freshness or health policy.
105105
OracleHeartbeatUnhealthy = 85,
106-
/// Early cash-out feature is disabled or not configured
107-
EarlyCashoutDisabled = 79,
108-
/// User does not have an active position to cash out
109-
PositionNotFound = 80,
110-
/// Early cash-out attempted outside the valid running phase
111-
InvalidPhaseForCashout = 81,
112-
/// Early cash-out is only supported for UpDown rounds
113-
WrongModeForCashout = 82,
114-
ProposalNotFound = 83,
115-
ProposalExpired = 84,
116-
GovInvalidState = 85,
117-
GovUnauthorized = 86,
118106
/// claim_many batch size exceeds MAX_CLAIM_BATCH_SIZE (Issue #277)
119107
ClaimBatchTooLarge = 87,
120108
/// claim_many batch contains the same address more than once (Issue #277)
121109
DuplicateClaimAddress = 88,
122-
/// Caller is denylisted, or allowlist mode is enabled and caller is not
123-
/// allowlisted (Issue #274 access-control gate).
124-
AccessDenied = 89,
125-
/// Oracle heartbeat is not live and strict mode blocks single-feed
126-
/// settlement (Issue #264 sibling check for `resolve_round`).
127-
OracleHeartbeatUnhealthy = 90,
110+
/// Early cash-out feature is disabled or not configured
111+
EarlyCashoutDisabled = 95,
112+
/// User does not have an active position to cash out
113+
PositionNotFound = 96,
114+
/// Early cash-out attempted outside the valid running phase
115+
InvalidPhaseForCashout = 97,
116+
/// Early cash-out is only supported for UpDown rounds
117+
WrongModeForCashout = 98,
118+
/// A proposed insurance payout split does not sum to the covered balance.
119+
InsuranceInvalidSplit = 99,
120+
/// The insurance backstop fund has insufficient balance to cover the claim.
121+
InsuranceInsufficientFund = 100,
122+
/// The supplied token amount is invalid for the requested operation.
123+
InvalidAmount = 101,
128124
/// The dispute window for `void_round` has expired, or dispute windows
129125
/// are not configured (`dispute_ledgers == 0`).
130126
DisputeWindowExpired = 91,

contracts/src/governance.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ pub fn execute(env: Env, executor: Address, proposal_id: u64) -> Result<(), Cont
309309
_extend_persistent_ttl(&env, &DataKeyCore::Oracle);
310310
}
311311
GovAction::WithdrawInsuranceFund(recipient, amount) => {
312-
crate::insurance::execute_withdraw_insurance_fund(env, &recipient, *amount)?;
312+
crate::insurance::execute_withdraw_insurance_fund(&env, &recipient, *amount)?;
313313
}
314314
GovAction::SetInsuranceSplitBps(bps) => {
315315
crate::insurance::set_insurance_split_bps(env.clone(), *bps)?;

contracts/src/queries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ pub fn get_user_archive_history(
256256

257257
let total = user_rounds.len();
258258
if offset >= total {
259-
return Vec::new(env_ref);
259+
return Ok(Vec::new(env_ref));
260260
}
261261

262262
let start = total.saturating_sub(offset + 1);

contracts/src/settlement.rs

Lines changed: 3 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::common::{
1414
use crate::config::{
1515
_apply_protocol_fee_precision, _apply_protocol_fee_updown, _read_fee_model,
1616
};
17-
use crate::config::{_apply_protocol_fee_precision, _apply_protocol_fee_updown, _read_fee_model};
1817
use crate::errors::ContractError;
1918
use crate::settlement_math::{
2019
classify_price_direction, compute_deviation_bps, compute_updown_winner_payout,
@@ -24,9 +23,9 @@ use crate::storage::clear_round_storage;
2423
use crate::types::{
2524
ArchivedRoundSummary, BetSide, DataKeyCore, DataKeyScoped, DeviationReferenceMode,
2625
HbGateConfig, LeaderboardEntry, MultiFeedPayload, OracleHeartbeatRecord, OraclePayload,
27-
OracleQuorumConfig, PendingWinningsUpdatedAtKey, PrecisionCommitment, PrecisionPayoutPolicy,
28-
PrecisionPrediction, PriceSample, Round, RoundArchiveStatus, RoundMode, TwapSamplesKey,
29-
UserOutcomeType, UserPosition, UserRoundOutcome, UserStats,
26+
OracleQuorumConfig, OneSidedPolicy, PendingWinningsUpdatedAtKey, PrecisionCommitment,
27+
PrecisionPayoutPolicy, PrecisionPrediction, PriceSample, Round, RoundArchiveStatus, RoundMode,
28+
TwapSamplesKey, UserOutcomeType, UserPosition, UserRoundOutcome, UserStats,
3029
};
3130
use soroban_sdk::xdr::ToXdr;
3231
use soroban_sdk::{contracttype, symbol_short, Address, Bytes, Env, Map, Symbol, Vec};
@@ -2856,244 +2855,5 @@ pub fn _update_stats_loss(env: &Env, user: Address) -> Result<(), ContractError>
28562855
crate::leaderboard::_update_leaderboards(env, user.clone());
28572856
crate::leaderboard::_update_season_stats_loss(env, user)?;
28582857
Ok(())
2859-
}
2860-
2861-
// ─── Dispute window (void / finalize) ───────────────────────────────────────
2862-
2863-
fn _resolved_at_map_key() -> Symbol {
2864-
Symbol::new(&Env::default(), "RslvAtMap")
2865-
}
2866-
2867-
fn _settlement_map_key() -> Symbol {
2868-
Symbol::new(&Env::default(), "SttlMap")
2869-
}
2870-
2871-
fn _read_resolved_at(env: &Env, round_id: u64) -> Option<u32> {
2872-
env.storage()
2873-
.persistent()
2874-
.get::<_, Map<u64, u32>>(&_resolved_at_map_key())
2875-
.and_then(|m| m.get(round_id))
2876-
}
2877-
2878-
fn _remove_resolved_at(env: &Env, round_id: u64) {
2879-
let key = _resolved_at_map_key();
2880-
let mut m: Map<u64, u32> = env
2881-
.storage()
2882-
.persistent()
2883-
.get(&key)
2884-
.unwrap_or(Map::new(env));
2885-
m.remove(round_id);
2886-
if m.len() == 0 {
2887-
env.storage().persistent().remove(&key);
2888-
} else {
2889-
env.storage().persistent().set(&key, &m);
2890-
_extend_ttl_symbol(env, &key);
2891-
}
2892-
}
2893-
2894-
fn _read_settlement(env: &Env, round_id: u64) -> Option<RoundSettlement> {
2895-
env.storage()
2896-
.persistent()
2897-
.get::<_, Map<u64, RoundSettlement>>(&_settlement_map_key())
2898-
.and_then(|m| m.get(round_id))
2899-
}
2900-
2901-
fn _remove_settlement(env: &Env, round_id: u64) {
2902-
let key = _settlement_map_key();
2903-
let mut m: Map<u64, RoundSettlement> = env
2904-
.storage()
2905-
.persistent()
2906-
.get(&key)
2907-
.unwrap_or(Map::new(env));
2908-
m.remove(round_id);
2909-
if m.len() == 0 {
2910-
env.storage().persistent().remove(&key);
2911-
} else {
2912-
env.storage().persistent().set(&key, &m);
2913-
_extend_ttl_symbol(env, &key);
2914-
}
2915-
}
2916-
2917-
fn _round_from_settlement(stl: &RoundSettlement) -> Round {
2918-
Round {
2919-
round_id: stl.round_id,
2920-
price_start: stl.price_start,
2921-
start_ledger: 0,
2922-
start_timestamp: 0,
2923-
bet_end_ledger: 0,
2924-
end_ledger: 0,
2925-
pool_up: stl.pool_up,
2926-
pool_down: stl.pool_down,
2927-
mode: if stl.mode == 0 {
2928-
RoundMode::UpDown
2929-
} else {
2930-
RoundMode::Precision
2931-
},
2932-
}
2933-
}
2934-
2935-
pub fn void_round(env: Env, round_id: u64) -> Result<(), ContractError> {
2936-
_require_supported_schema(&env)?;
2937-
_ensure_not_paused(&env)?;
2938-
2939-
let dispute_ledgers = crate::config::get_dispute_ledgers(&env);
2940-
if dispute_ledgers == 0 {
2941-
return Err(ContractError::DisputeWindowExpired);
2942-
}
2943-
2944-
let resolved_at: u32 = _read_resolved_at(&env, round_id).ok_or(ContractError::DisputeWindowExpired)?;
2945-
if env.ledger().sequence() >= resolved_at.saturating_add(dispute_ledgers) {
2946-
return Err(ContractError::DisputeWindowExpired);
2947-
}
2948-
2949-
let participants: Vec<Address> = env
2950-
.storage()
2951-
.persistent()
2952-
.get(&DataKeyScoped::RoundParticipants(round_id))
2953-
.unwrap_or(Vec::new(&env));
2954-
if participants.is_empty() {
2955-
return Err(ContractError::NoActiveRound);
2956-
}
2957-
2958-
let settlement: RoundSettlement =
2959-
_read_settlement(&env, round_id).ok_or(ContractError::NoActiveRound)?;
2960-
2961-
for i in 0..participants.len() {
2962-
if let Some(user) = participants.get(i) {
2963-
let pos_key = DataKeyScoped::Position(round_id, user.clone());
2964-
if let Some(pos) = env.storage().persistent().get::<_, UserPosition>(&pos_key) {
2965-
_accumulate_pending(&env, user.clone(), pos.amount)?;
2966-
let side = match pos.side {
2967-
BetSide::Up => 0,
2968-
BetSide::Down => 1,
2969-
};
2970-
_persist_user_outcome(
2971-
&env, round_id, 0, &user, side, 0, pos.amount, pos.amount,
2972-
UserOutcomeType::Refund,
2973-
);
2974-
}
2975-
let pred_key = DataKeyScoped::PrecisionPosition(round_id, user.clone());
2976-
let commit_key = DataKeyScoped::PrecisionCommitment(round_id, user.clone());
2977-
if let Some(pred) = env.storage().persistent().get::<_, PrecisionPrediction>(&pred_key) {
2978-
_accumulate_pending(&env, user.clone(), pred.amount)?;
2979-
_persist_user_outcome(
2980-
&env, round_id, 1, &user, 2, pred.predicted_price, pred.amount, pred.amount,
2981-
UserOutcomeType::Refund,
2982-
);
2983-
} else if let Some(commit) =
2984-
env.storage().persistent().get::<_, PrecisionCommitment>(&commit_key)
2985-
{
2986-
_accumulate_pending(&env, user.clone(), commit.amount)?;
2987-
_persist_user_outcome(
2988-
&env, round_id, 1, &user, 2, 0, commit.amount, commit.amount,
2989-
UserOutcomeType::Refund,
2990-
);
2991-
}
2992-
}
2993-
}
2994-
2995-
for i in 0..participants.len() {
2996-
if let Some(user) = participants.get(i) {
2997-
env.storage().persistent().remove(&DataKeyScoped::Position(round_id, user.clone()));
2998-
env.storage().persistent().remove(&DataKeyScoped::PrecisionPosition(round_id, user.clone()));
2999-
env.storage().persistent().remove(&DataKeyScoped::PrecisionCommitment(round_id, user));
3000-
}
3001-
}
3002-
env.storage().persistent().remove(&DataKeyScoped::RoundParticipants(round_id));
3003-
3004-
let round = _round_from_settlement(&settlement);
3005-
_archive_round(
3006-
&env,
3007-
&round,
3008-
RoundArchiveStatus::Voided,
3009-
settlement.final_price,
3010-
&participants,
3011-
settlement.fee_amount,
3012-
None,
3013-
);
3014-
3015-
_remove_settlement(&env, round_id);
3016-
_remove_resolved_at(&env, round_id);
3017-
3018-
#[allow(deprecated)]
3019-
env.events().publish(
3020-
(symbol_short!("round"), symbol_short!("voided")),
3021-
(round_id, settlement.final_price, participants.len() as u32, settlement.fee_amount),
3022-
);
3023-
Ok(())
3024-
}
3025-
3026-
pub fn finalize_round(env: Env, round_id: u64) -> Result<(), ContractError> {
3027-
_require_supported_schema(&env)?;
3028-
_ensure_not_paused(&env)?;
3029-
3030-
let dispute_ledgers = crate::config::get_dispute_ledgers(&env);
3031-
if dispute_ledgers == 0 {
3032-
return Err(ContractError::DisputeWindowExpired);
3033-
}
3034-
3035-
let resolved_at: u32 = _read_resolved_at(&env, round_id).ok_or(ContractError::DisputeWindowExpired)?;
3036-
if env.ledger().sequence() < resolved_at.saturating_add(dispute_ledgers) {
3037-
return Err(ContractError::ClaimLocked);
3038-
}
30392858

3040-
let participants: Vec<Address> = env
3041-
.storage()
3042-
.persistent()
3043-
.get(&DataKeyScoped::RoundParticipants(round_id))
3044-
.unwrap_or(Vec::new(&env));
3045-
if participants.is_empty() {
3046-
return Err(ContractError::NoActiveRound);
3047-
}
3048-
3049-
let settlement: RoundSettlement =
3050-
_read_settlement(&env, round_id).ok_or(ContractError::NoActiveRound)?;
3051-
3052-
for i in 0..settlement.participants.len() {
3053-
if let Some(entry) = settlement.participants.get(i) {
3054-
match entry.outcome {
3055-
UserOutcomeType::Win => {
3056-
_accumulate_pending(&env, entry.user.clone(), entry.payout)?;
3057-
_update_stats_win(&env, entry.user.clone())?;
3058-
}
3059-
UserOutcomeType::Loss => {
3060-
_update_stats_loss(&env, entry.user.clone())?;
3061-
}
3062-
UserOutcomeType::Refund => {
3063-
_accumulate_pending(&env, entry.user.clone(), entry.payout)?;
3064-
}
3065-
_ => {}
3066-
}
3067-
}
3068-
}
3069-
3070-
for i in 0..participants.len() {
3071-
if let Some(user) = participants.get(i) {
3072-
env.storage().persistent().remove(&DataKeyScoped::Position(round_id, user.clone()));
3073-
env.storage().persistent().remove(&DataKeyScoped::PrecisionPosition(round_id, user.clone()));
3074-
env.storage().persistent().remove(&DataKeyScoped::PrecisionCommitment(round_id, user));
3075-
}
3076-
}
3077-
env.storage().persistent().remove(&DataKeyScoped::RoundParticipants(round_id));
3078-
3079-
let round = _round_from_settlement(&settlement);
3080-
_archive_round(
3081-
&env,
3082-
&round,
3083-
RoundArchiveStatus::Resolved,
3084-
settlement.final_price,
3085-
&participants,
3086-
settlement.fee_amount,
3087-
None,
3088-
);
3089-
3090-
_remove_settlement(&env, round_id);
3091-
_remove_resolved_at(&env, round_id);
3092-
3093-
#[allow(deprecated)]
3094-
env.events().publish(
3095-
(symbol_short!("round"), symbol_short!("finalized")),
3096-
(round_id, settlement.final_price, participants.len() as u32, settlement.fee_amount),
3097-
);
3098-
Ok(())
30992859
}

docs/wasm-size-budget.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
Stored in `.wasm-size-baseline` at the repo root (plain integer, bytes).
55

66
### Budget policy
7-
CI allows up to **+10%** growth above baseline before failing.
7+
CI allows up to **+5%** growth above baseline before failing.
88

99
### How CI checks it
1010
The `wasm-size-gate` job in `.github/workflows/ci.yml`:
1111
1. Builds `xelma_contract.wasm` in release mode
1212
2. Runs `scripts/check_wasm_size.sh` against that artifact, which compares
13-
size against `.wasm-size-baseline` + 10%, prints a size report, and fails
13+
size against `.wasm-size-baseline` + 5%, prints a size report, and fails
1414
with an actionable error if the budget is exceeded
1515

1616
### Checking it locally

scripts/check_wasm_size.sh

100644100755
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ fi
4545

4646
WASM_SIZE=$(wc -c < "$WASM_PATH")
4747
BASELINE=$(cat "$BASELINE_FILE")
48-
BUDGET=$(( BASELINE + BASELINE / 10 ))
48+
BUDGET=$(( BASELINE + BASELINE / 20 ))
4949

5050
echo "WASM size report"
5151
echo " File : $WASM_PATH"
5252
echo " Current : ${WASM_SIZE} bytes"
5353
echo " Baseline: ${BASELINE} bytes"
54-
echo " Budget : ${BUDGET} bytes (+10%)"
54+
echo " Budget : ${BUDGET} bytes (+5%)"
5555
echo " Delta : $(( WASM_SIZE - BASELINE )) bytes"
5656

5757
if [ "${WASM_SIZE}" -gt "${BUDGET}" ]; then

0 commit comments

Comments
 (0)