Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions contracts/adl_handler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pub enum Error {
/// Max PnL factor for ADL is not configured (0) for the requested market/side.
/// Callers must set a non-zero value via DataStore before ADL can be evaluated.
MissingMaxPnlConfig = 8,
/// Issue #643: update_oracle called with the current oracle address
/// (no-op) or with this contract's own address / another of its stored
/// instance addresses (order_handler, data_store, role_store, admin) —
/// almost certainly a copy-paste mistake, not an intentional rotation.
InvalidOracle = 10,
}

// ─── External clients ─────────────────────────────────────────────────────────
Expand Down Expand Up @@ -126,6 +131,46 @@ impl AdlHandler {
.set(&InstanceKey::OrderHandler, &order_handler);
}

pub fn update_oracle(env: Env, caller: Address, new_oracle: Address) {
caller.require_auth();
let admin: Address = env
.storage()
.instance()
.get(&InstanceKey::Admin)
.unwrap_or_else(|| panic_with_error!(&env, Error::NotInitialized));
if caller != admin {
panic_with_error!(&env, Error::Unauthorized);
}
// Issue #643: reject a no-op call and the most likely copy-paste
// mistakes — pointing the oracle at this contract itself or at one
// of its own other stored instance addresses.
if new_oracle == env.current_contract_address() {
panic_with_error!(&env, Error::InvalidOracle);
}
for key in [
InstanceKey::Oracle,
InstanceKey::Admin,
InstanceKey::RoleStore,
InstanceKey::DataStore,
InstanceKey::OrderHandler,
] {
if let Some(other) = env.storage().instance().get::<_, Address>(&key) {
if other == new_oracle {
panic_with_error!(&env, Error::InvalidOracle);
}
}
}
let old_oracle: Address = env.storage().instance().get(&InstanceKey::Oracle).unwrap();
env.storage().instance().set(&InstanceKey::Oracle, &new_oracle);
// Issue #605: the oracle address every subsequent price validation
// relies on just changed — emit an event so off-chain monitoring has
// an audit trail for this admin action.
env.events().publish(
(symbol_short!("orcl_set"),),
(old_oracle, new_oracle),
);
}

/// Check whether ADL is currently required for the given market side.
///
/// Returns true if total trader PnL / pool_value > MAX_PNL_FACTOR_FOR_ADL.
Expand Down
135 changes: 88 additions & 47 deletions contracts/data_store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,23 @@ impl DataStore {
// ── u128 operations ──────────────────────────────────────────────────────

pub fn get_u128(env: Env, key: BytesN<32>) -> u128 {
let dk = DataKey::U128(key);
let val: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
env.storage()
.persistent()
.get(&DataKey::U128(key))
.unwrap_or(0)
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
val
}

/// Read multiple u128 values in one call to reduce cross-contract call overhead.
pub fn get_u128_batch(env: Env, keys: Vec<BytesN<32>>) -> Vec<u128> {
let mut results = Vec::new(&env);
for key in keys.iter() {
let val: u128 = env
.storage()
let dk = DataKey::U128(key);
let val: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
env.storage()
.persistent()
.get(&DataKey::U128(key))
.unwrap_or(0);
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
results.push_back(val);
}
results
Expand Down Expand Up @@ -234,7 +236,11 @@ impl DataStore {
pub fn set_u128(env: Env, caller: Address, key: BytesN<32>, value: u128) -> u128 {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::U128(key), &value);
let dk = DataKey::U128(key);
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
value
}

Expand All @@ -248,7 +254,11 @@ impl DataStore {
pub fn set_u128_config(env: Env, caller: Address, key: BytesN<32>, value: u128) -> u128 {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::U128(key.clone()), &value);
let dk = DataKey::U128(key.clone());
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
env.storage().instance().set(&DataKey::InstanceU128(key), &value);
value
}
Expand All @@ -266,11 +276,11 @@ impl DataStore {
/// `set_u128_config`, the next `get_u128_cached` call will detect the
/// divergence and return the fresh persistent value, updating the cache.
pub fn get_u128_cached(env: Env, key: BytesN<32>) -> u128 {
let persistent_val: u128 = env
.storage()
let dk = DataKey::U128(key.clone());
let persistent_val: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
env.storage()
.persistent()
.get(&DataKey::U128(key.clone()))
.unwrap_or(0);
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
env.storage()
.instance()
.set(&DataKey::InstanceU128(key), &persistent_val);
Expand Down Expand Up @@ -305,11 +315,8 @@ impl DataStore {
pub fn apply_delta_to_u128(env: Env, caller: Address, key: BytesN<32>, delta: i128) -> u128 {
caller.require_auth();
require_controller(&env, &caller);
let current: u128 = env
.storage()
.persistent()
.get(&DataKey::U128(key.clone()))
.unwrap_or(0);
let dk = DataKey::U128(key);
let current: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
let next = if delta >= 0 {
current.saturating_add(delta as u128)
} else {
Expand All @@ -319,52 +326,61 @@ impl DataStore {
}
current - sub
};
env.storage().persistent().set(&DataKey::U128(key), &next);
env.storage().persistent().set(&dk, &next);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
next
}

pub fn increment_u128(env: Env, caller: Address, key: BytesN<32>, amount: u128) -> u128 {
caller.require_auth();
require_controller(&env, &caller);
let current: u128 = env
.storage()
.persistent()
.get(&DataKey::U128(key.clone()))
.unwrap_or(0);
let dk = DataKey::U128(key);
let current: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
let next = current.saturating_add(amount);
env.storage().persistent().set(&DataKey::U128(key), &next);
env.storage().persistent().set(&dk, &next);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
next
}

pub fn decrement_u128(env: Env, caller: Address, key: BytesN<32>, amount: u128) -> u128 {
caller.require_auth();
require_controller(&env, &caller);
let current: u128 = env
.storage()
.persistent()
.get(&DataKey::U128(key.clone()))
.unwrap_or(0);
let dk = DataKey::U128(key);
let current: u128 = env.storage().persistent().get(&dk).unwrap_or(0);
if amount > current {
panic_with_error!(&env, Error::Underflow);
}
let next = current - amount;
env.storage().persistent().set(&DataKey::U128(key), &next);
env.storage().persistent().set(&dk, &next);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
next
}

// ── i128 operations ──────────────────────────────────────────────────────

pub fn get_i128(env: Env, key: BytesN<32>) -> i128 {
let dk = DataKey::I128(key);
let val: i128 = env.storage().persistent().get(&dk).unwrap_or(0);
env.storage()
.persistent()
.get(&DataKey::I128(key))
.unwrap_or(0)
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
val
}

pub fn set_i128(env: Env, caller: Address, key: BytesN<32>, value: i128) -> i128 {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::I128(key), &value);
let dk = DataKey::I128(key);
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
value
}

Expand All @@ -377,26 +393,35 @@ impl DataStore {
pub fn apply_delta_to_i128(env: Env, caller: Address, key: BytesN<32>, delta: i128) -> i128 {
caller.require_auth();
require_controller(&env, &caller);
let current: i128 = env
.storage()
.persistent()
.get(&DataKey::I128(key.clone()))
.unwrap_or(0);
let dk = DataKey::I128(key);
let current: i128 = env.storage().persistent().get(&dk).unwrap_or(0);
let next = current.saturating_add(delta);
env.storage().persistent().set(&DataKey::I128(key), &next);
env.storage().persistent().set(&dk, &next);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
next
}

// ── Address operations ────────────────────────────────────────────────────

pub fn get_address(env: Env, key: BytesN<32>) -> Option<Address> {
env.storage().persistent().get(&DataKey::Addr(key))
let dk = DataKey::Addr(key);
let val: Option<Address> = env.storage().persistent().get(&dk);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
val
}

pub fn set_address(env: Env, caller: Address, key: BytesN<32>, value: Address) -> Address {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::Addr(key), &value);
let dk = DataKey::Addr(key);
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
value
}

Expand All @@ -409,16 +434,22 @@ impl DataStore {
// ── bool operations ───────────────────────────────────────────────────────

pub fn get_bool(env: Env, key: BytesN<32>) -> bool {
let dk = DataKey::Bool(key);
let val: bool = env.storage().persistent().get(&dk).unwrap_or(false);
env.storage()
.persistent()
.get(&DataKey::Bool(key))
.unwrap_or(false)
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
val
}

pub fn set_bool(env: Env, caller: Address, key: BytesN<32>, value: bool) -> bool {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::Bool(key), &value);
let dk = DataKey::Bool(key);
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
value
}

Expand All @@ -431,10 +462,16 @@ impl DataStore {
// ── BytesN<32> operations ─────────────────────────────────────────────────

pub fn get_bytes32(env: Env, key: BytesN<32>) -> BytesN<32> {
let dk = DataKey::B32(key);
let val: BytesN<32> = env
.storage()
.persistent()
.get(&dk)
.unwrap_or(BytesN::from_array(&env, &[0u8; 32]));
env.storage()
.persistent()
.get(&DataKey::B32(key))
.unwrap_or(BytesN::from_array(&env, &[0u8; 32]))
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
val
}

pub fn set_bytes32(
Expand All @@ -445,7 +482,11 @@ impl DataStore {
) -> BytesN<32> {
caller.require_auth();
require_controller(&env, &caller);
env.storage().persistent().set(&DataKey::B32(key), &value);
let dk = DataKey::B32(key);
env.storage().persistent().set(&dk, &value);
env.storage()
.persistent()
.extend_ttl(&dk, MIN_BUMP_THRESHOLD, PERSISTENT_BUMP_TARGET);
value
}

Expand Down
8 changes: 4 additions & 4 deletions contracts/deposit_handler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ trait IOracle {
trait IDepositVault {
fn transfer_out(env: Env, caller: Address, token: Address, receiver: Address, amount: i128);
fn get_recorded_balance(env: Env, token: Address) -> i128;
fn record_transfer_in(env: Env, token: Address) -> i128;
fn record_transfer_in(env: Env, caller: Address, token: Address) -> i128;
}

#[allow(dead_code)]
Expand Down Expand Up @@ -316,15 +316,15 @@ impl DepositHandler {
);
// Snapshot the vault's balance so execute_deposit's recorded-balance
// check (guard against fee-on-transfer tokens) sees the funds just received.
vault_client.record_transfer_in(&params.initial_long_token);
vault_client.record_transfer_in(&handler, &params.initial_long_token);
}
if params.short_token_amount > 0 {
token::Client::new(&env, &params.initial_short_token).transfer(
&caller,
&deposit_vault,
&params.short_token_amount,
);
vault_client.record_transfer_in(&params.initial_short_token);
vault_client.record_transfer_in(&handler, &params.initial_short_token);
}

// Issue #370: collect execution_fee in the long token.
Expand All @@ -334,7 +334,7 @@ impl DepositHandler {
&deposit_vault,
&exec_fee,
);
vault_client.record_transfer_in(&params.initial_long_token);
vault_client.record_transfer_in(&handler, &params.initial_long_token);
}

// Allocate deposit key from nonce
Expand Down
6 changes: 5 additions & 1 deletion contracts/deposit_vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ impl DepositVault {
/// Snapshot the balance of `token` in this vault.
/// Returns the amount received since the last snapshot (delta).
/// Called by deposit_handler right after the user's transfer lands.
pub fn record_transfer_in(env: Env, token: Address) -> i128 {
/// Only callable by a CONTROLLER (deposit_handler).
pub fn record_transfer_in(env: Env, caller: Address, token: Address) -> i128 {
caller.require_auth();
require_controller(&env, &caller);

let current = token::Client::new(&env, &token).balance(&env.current_contract_address());
let recorded: i128 = env
.storage()
Expand Down
Loading
Loading