Skip to content

Commit

Permalink
feat: pausable contract
Browse files Browse the repository at this point in the history
  • Loading branch information
gluax authored and Thomasvdam committed Jan 29, 2025
1 parent ec5b614 commit a1d3836
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ lazy_static = "1.4"
libfuzzer-sys = "0.4"
rand = "0.8"
schemars = { version = "0.8", features = ["semver"] }
seda-common = { git = "https://github.com/sedaprotocol/seda-common-rs.git", tag = "v0.5.0" }
seda-common = { git = "https://github.com/sedaprotocol/seda-common-rs.git", tag = "v0.5.1" }
# leaving this in to make local development easier
# seda-common = { path = "../seda-common-rs/crates/common" }
semver = { version = "1.0", features = ["serde"] }
Expand Down
7 changes: 6 additions & 1 deletion contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
QueryHandler,
SudoHandler,
},
state::{CHAIN_ID, TOKEN},
state::{CHAIN_ID, PAUSED, TOKEN},
};

// version info for migration info
Expand All @@ -45,6 +45,7 @@ pub fn instantiate(
OWNER.save(deps.storage, &deps.api.addr_validate(&msg.owner)?)?;
CHAIN_ID.save(deps.storage, &msg.chain_id)?;
PENDING_OWNER.save(deps.storage, &None)?;
PAUSED.save(deps.storage, &false)?;

let init_staking_config = msg.staking_config.unwrap_or(StakingConfig {
minimum_stake_to_register: INITIAL_MINIMUM_STAKE_TO_REGISTER,
Expand Down Expand Up @@ -83,6 +84,10 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> R

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn sudo(deps: DepsMut, env: Env, sudo: SudoMsg) -> Result<Response, ContractError> {
if PAUSED.load(deps.storage)? {
return Err(ContractError::ContractPaused("sudo messages".to_string()));
}

sudo.sudo(deps, env)
}

Expand Down
4 changes: 4 additions & 0 deletions contract/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ pub enum ContractError {
InvalidHashLength(usize),
#[error("Invalid public key length `{0}` expected 33 bytes")]
InvalidPublicKeyLength(usize),
#[error("Contract paused: cannot perform operation `{0}`")]
ContractPaused(String),
#[error("Contract not paused: cannot unpause")]
ContractNotPaused,
}

#[cfg(test)]
Expand Down
6 changes: 6 additions & 0 deletions contract/src/msgs/data_requests/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ use super::{
msgs::data_requests::{execute::commit_result, query::QueryMsg},
*,
};
use crate::state::PAUSED;

impl QueryHandler for QueryMsg {
fn query(self, deps: Deps, env: Env) -> Result<Binary, ContractError> {
let contract_paused = PAUSED.load(deps.storage)?;

let binary = match self {
QueryMsg::CanExecutorCommit {
dr_id,
Expand Down Expand Up @@ -52,6 +55,9 @@ impl QueryHandler for QueryMsg {
let reveals = dr.map(|dr| dr.reveals).unwrap_or_default();
to_json_binary(&reveals)?
}
QueryMsg::GetDataRequestsByStatus { .. } if contract_paused => {
to_json_binary::<Vec<DataRequest>>(&Vec::with_capacity(0))?
}
QueryMsg::GetDataRequestsByStatus { status, offset, limit } => {
to_json_binary(&state::requests_by_status(deps.storage, &status, offset, limit)?)?
}
Expand Down
4 changes: 4 additions & 0 deletions contract/src/msgs/owner/execute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use super::{

pub(in crate::msgs::owner) mod accept_ownership;
pub(in crate::msgs::owner) mod add_to_allowlist;
pub mod pause;
pub(in crate::msgs::owner) mod remove_from_allowlist;
pub(in crate::msgs::owner) mod transfer_ownership;
pub mod unpause;

impl ExecuteHandler for ExecuteMsg {
fn execute(self, deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response, ContractError> {
Expand All @@ -17,6 +19,8 @@ impl ExecuteHandler for ExecuteMsg {
ExecuteMsg::AcceptOwnership(msg) => msg.execute(deps, env, info),
ExecuteMsg::AddToAllowlist(msg) => msg.execute(deps, env, info),
ExecuteMsg::RemoveFromAllowlist(msg) => msg.execute(deps, env, info),
ExecuteMsg::Pause(msg) => msg.execute(deps, env, info),
ExecuteMsg::Unpause(msg) => msg.execute(deps, env, info),
}
}
}
29 changes: 29 additions & 0 deletions contract/src/msgs/owner/execute/pause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use cosmwasm_std::{DepsMut, Env, Event, MessageInfo, Response};
use seda_common::msgs::owner::execute;

use crate::{
contract::CONTRACT_VERSION,
error::ContractError,
msgs::{owner::state::OWNER, ExecuteHandler},
state::PAUSED,
};

impl ExecuteHandler for execute::pause::Execute {
fn execute(self, deps: DepsMut, _: Env, info: MessageInfo) -> Result<Response, ContractError> {
// require the sender to be the OWNER
let owner = OWNER.load(deps.storage)?;
if info.sender != owner {
return Err(ContractError::NotOwner);
}

let paused = PAUSED.load(deps.storage)?;
if paused {
return Err(ContractError::ContractPaused("pause".to_string()));
}

PAUSED.save(deps.storage, &true)?;

Ok(Response::new().add_events([Event::new("seda-pause-contract")
.add_attributes([("version", CONTRACT_VERSION.to_string()), ("paused", true.to_string())])]))
}
}
29 changes: 29 additions & 0 deletions contract/src/msgs/owner/execute/unpause.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use cosmwasm_std::{DepsMut, Env, Event, MessageInfo, Response};
use seda_common::msgs::owner::execute;

use crate::{
contract::CONTRACT_VERSION,
error::ContractError,
msgs::{owner::state::OWNER, ExecuteHandler},
state::PAUSED,
};

impl ExecuteHandler for execute::unpause::Execute {
fn execute(self, deps: DepsMut, _: Env, info: MessageInfo) -> Result<Response, ContractError> {
// require the sender to be the OWNER
let owner = OWNER.load(deps.storage)?;
if info.sender != owner {
return Err(ContractError::NotOwner);
}

let paused = PAUSED.load(deps.storage)?;
if !paused {
return Err(ContractError::ContractNotPaused);
}

PAUSED.save(deps.storage, &false)?;

Ok(Response::new().add_events([Event::new("seda-pause-contract")
.add_attributes([("version", CONTRACT_VERSION.to_string()), ("paused", false.to_string())])]))
}
}
2 changes: 2 additions & 0 deletions contract/src/msgs/owner/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use super::{
state::{OWNER, PENDING_OWNER},
*,
};
use crate::state::PAUSED;

impl QueryHandler for QueryMsg {
fn query(self, deps: Deps, _env: Env) -> Result<Binary, ContractError> {
let binary = match self {
QueryMsg::GetOwner {} => to_json_binary(&OWNER.load(deps.storage)?)?,
QueryMsg::GetPendingOwner {} => to_json_binary(&PENDING_OWNER.load(deps.storage)?)?,
QueryMsg::IsPaused {} => to_json_binary(&PAUSED.load(deps.storage)?)?,
};

Ok(binary)
Expand Down
3 changes: 3 additions & 0 deletions contract/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use cw_storage_plus::{Item, Map};

use crate::types::PublicKey;

/// Flag to indicate if the contract is paused.
pub const PAUSED: Item<bool> = Item::new("paused");

/// Token denom used for staking (e.g., `aseda`).
pub const TOKEN: Item<String> = Item::new("token");

Expand Down

0 comments on commit a1d3836

Please sign in to comment.