Skip to content
Open
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
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.

6 changes: 6 additions & 0 deletions contracts/reserve-auction/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ pub enum ContractError {
#[error("InvalidReservePrice: {min}")]
InvalidReservePrice { min: Coin },

#[error("InvalidMinReservePriceManagerAddress")]
InvalidMinReservePriceManagerAddress {},

#[error("BidTooLow: {0}")]
BidTooLow(Uint128),

Expand All @@ -55,4 +58,7 @@ pub enum ContractError {

#[error("Auction within halt window: a halt has occurred and the auction cannot be settled")]
AuctionWithinHaltWindow,

#[error("Token is blacklisted: collection={0}, token_id={1}")]
TokenBlacklisted(String, String),
}
198 changes: 193 additions & 5 deletions contracts/reserve-auction/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use std::vec;

use crate::error::ContractError;
use crate::helpers::{only_no_auction, settle_auction, validate_reserve_price};
use crate::helpers::{
build_blacklist_key, only_blacklist_manager, only_min_reserve_price_manager, only_no_auction,
only_not_blacklisted, settle_auction, validate_reserve_price, BLACKLIST_MANAGER,
};
use crate::msg::ExecuteMsg;
use crate::state::{auctions, Auction, HighBid};
use crate::state::{CONFIG, HALT_MANAGER};
use crate::state::{
auctions, Auction, HighBid, BLACKLIST, CONFIG, HALT_MANAGER, MIN_RESERVE_PRICES,
MIN_RESERVE_PRICE_MANAGER,
};
use cosmwasm_std::{
attr, coin, ensure, ensure_eq, has_coins, Addr, Coin, DepsMut, Env, Event, MessageInfo,
Timestamp,
Expand Down Expand Up @@ -76,6 +81,36 @@ pub fn execute(
api.addr_validate(&collection)?,
&token_id,
),
ExecuteMsg::SetMinReservePrices { min_reserve_prices } => {
execute_set_min_reserve_prices(deps, info, min_reserve_prices)
}
ExecuteMsg::UnsetMinReservePrices { denoms } => {
execute_unset_min_reserve_prices(deps, info, denoms)
}
ExecuteMsg::UpdateMinReservePriceManager { manager } => {
execute_update_min_reserve_price_manager(deps, info, manager)
}
ExecuteMsg::AddToBlacklist {
collection,
token_id,
} => execute_add_to_blacklist(deps, info, api.addr_validate(&collection)?, token_id),
ExecuteMsg::RemoveFromBlacklist {
collection,
token_id,
} => execute_remove_from_blacklist(deps, info, api.addr_validate(&collection)?, token_id),
ExecuteMsg::BatchAddToBlacklist {
collection,
token_ids,
} => execute_batch_add_to_blacklist(deps, info, api.addr_validate(&collection)?, token_ids),
ExecuteMsg::BatchRemoveFromBlacklist {
collection,
token_ids,
} => execute_batch_remove_from_blacklist(
deps,
info,
api.addr_validate(&collection)?,
token_ids,
),
}
}

Expand Down Expand Up @@ -109,6 +144,9 @@ pub fn execute_create_auction(

only_tradable(&deps.querier, &env.block, &collection)?;

// Check if token is blacklisted
only_not_blacklisted(deps.as_ref().storage, &collection, token_id)?;

let mut response = Response::new();

validate_reserve_price(deps.as_ref().storage, &reserve_price)?;
Expand Down Expand Up @@ -230,8 +268,12 @@ pub fn execute_cancel_auction(
nonpayable(&info)?;
let auction = auctions().load(deps.storage, (collection.clone(), token_id.to_string()))?;

// Ensure caller is the seller
ensure_eq!(auction.seller, info.sender, ContractError::Unauthorized {});
let is_blacklist_manager = info.sender.as_str() == BLACKLIST_MANAGER;

if !is_blacklist_manager {
// Ensure caller is the seller
ensure_eq!(auction.seller, info.sender, ContractError::Unauthorized {});
}

// Ensure auction hasn't started
ensure!(
Expand Down Expand Up @@ -267,6 +309,9 @@ pub fn execute_place_bid(

let mut auction = auctions().load(deps.storage, (collection, token_id.to_string()))?;

// Check if token is blacklisted
only_not_blacklisted(deps.as_ref().storage, &auction.collection, token_id)?;

let auction_denom = auction.denom();
let bid_amount = must_pay(&info, &auction_denom)?;

Expand Down Expand Up @@ -372,3 +417,146 @@ pub fn execute_settle_auction(

settle_auction(deps, block_time, auction, &config, &halt_manager, response)
}

pub fn execute_set_min_reserve_prices(
deps: DepsMut,
info: MessageInfo,
min_reserve_prices: Vec<Coin>,
) -> Result<Response, ContractError> {
only_min_reserve_price_manager(deps.as_ref(), &info.sender)?;

let mut response = Response::new();

for min_reserve_price in min_reserve_prices {
ensure!(
!MIN_RESERVE_PRICES.has(deps.storage, min_reserve_price.denom.clone()),
ContractError::InvalidInput("found duplicate denom".to_string())
);

MIN_RESERVE_PRICES.save(
deps.storage,
min_reserve_price.denom.clone(),
&min_reserve_price.amount,
)?;
response = response.add_event(
Event::new("set-min-reserve-price")
.add_attribute("denom", min_reserve_price.denom)
.add_attribute("amount", min_reserve_price.amount),
);
}
Ok(response)
}

pub fn execute_unset_min_reserve_prices(
deps: DepsMut,
info: MessageInfo,
denoms: Vec<String>,
) -> Result<Response, ContractError> {
only_min_reserve_price_manager(deps.as_ref(), &info.sender)?;

let mut response = Response::new();

for denom in denoms {
ensure!(
MIN_RESERVE_PRICES.has(deps.storage, denom.clone()),
ContractError::InvalidInput("denom not found".to_string())
);

MIN_RESERVE_PRICES.remove(deps.storage, denom.clone());
response =
response.add_event(Event::new("unset-min-reserve-price").add_attribute("denom", denom));
}
Ok(response)
}

pub fn execute_update_min_reserve_price_manager(
deps: DepsMut,
info: MessageInfo,
manager: String,
) -> Result<Response, ContractError> {
only_min_reserve_price_manager(deps.as_ref(), &info.sender)?;

MIN_RESERVE_PRICE_MANAGER.save(deps.storage, &deps.api.addr_validate(&manager)?)?;

Ok(Response::new().add_attribute("action", "update-min-reserve-price-manager"))
}

pub fn execute_add_to_blacklist(
deps: DepsMut,
info: MessageInfo,
collection: Addr,
token_id: String,
) -> Result<Response, ContractError> {
only_blacklist_manager(&info.sender)?;

let key = build_blacklist_key(collection.as_ref(), &token_id);
BLACKLIST.save(deps.storage, key, &())?;

let response = Response::new()
.add_attribute("action", "add-to-blacklist")
.add_attribute("collection", collection.to_string())
.add_attribute("token_id", token_id);

Ok(response)
}

pub fn execute_remove_from_blacklist(
deps: DepsMut,
info: MessageInfo,
collection: Addr,
token_id: String,
) -> Result<Response, ContractError> {
only_blacklist_manager(&info.sender)?;

let key = build_blacklist_key(collection.as_ref(), &token_id);
BLACKLIST.remove(deps.storage, key);

let response = Response::new()
.add_attribute("action", "remove-from-blacklist")
.add_attribute("collection", collection.to_string())
.add_attribute("token_id", token_id);

Ok(response)
}

pub fn execute_batch_add_to_blacklist(
deps: DepsMut,
info: MessageInfo,
collection: Addr,
token_ids: Vec<String>,
) -> Result<Response, ContractError> {
only_blacklist_manager(&info.sender)?;

for token_id in &token_ids {
let key = build_blacklist_key(collection.as_ref(), token_id);
BLACKLIST.save(deps.storage, key, &())?;
}

let response = Response::new()
.add_attribute("action", "batch-add-to-blacklist")
.add_attribute("collection", collection.to_string())
.add_attribute("count", token_ids.len().to_string());

Ok(response)
}

pub fn execute_batch_remove_from_blacklist(
deps: DepsMut,
info: MessageInfo,
collection: Addr,
token_ids: Vec<String>,
) -> Result<Response, ContractError> {
only_blacklist_manager(&info.sender)?;

for token_id in &token_ids {
let key = build_blacklist_key(collection.as_ref(), token_id);
BLACKLIST.remove(deps.storage, key);
}

let response = Response::new()
.add_attribute("action", "batch-remove-from-blacklist")
.add_attribute("collection", collection.to_string())
.add_attribute("count", token_ids.len().to_string());

Ok(response)
}
51 changes: 49 additions & 2 deletions contracts/reserve-auction/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use cosmwasm_std::{coin, ensure, Addr, Coin, Deps, DepsMut, Event, Storage, Timestamp};
use cosmwasm_std::{coin, ensure, ensure_eq, Addr, Coin, Deps, DepsMut, Event, Storage, Timestamp};
use sg_marketplace_common::{
nft::{load_collection_royalties, transfer_nft},
sale::payout_nft_sale_fees,
};
use sg_std::Response;

use crate::state::{auctions, Auction, Config, HaltManager, MIN_RESERVE_PRICES};
use crate::state::{
auctions, Auction, Config, HaltManager, BLACKLIST, MIN_RESERVE_PRICES,
MIN_RESERVE_PRICE_MANAGER,
};
use crate::ContractError;

pub const BLACKLIST_MANAGER: &str =
"stars14ay8uhnnacg79dvygpf5rh9wz2m7tj8jnw60w233xja4akax4mrs54770s";

pub fn only_no_auction(deps: Deps, collection: &Addr, token_id: &str) -> Result<(), ContractError> {
if auctions()
.may_load(deps.storage, (collection.clone(), token_id.to_string()))?
Expand All @@ -21,6 +27,44 @@ pub fn only_no_auction(deps: Deps, collection: &Addr, token_id: &str) -> Result<
Ok(())
}

pub fn only_min_reserve_price_manager(deps: Deps, sender: &Addr) -> Result<(), ContractError> {
let min_reserve_price_manager = MIN_RESERVE_PRICE_MANAGER.load(deps.storage)?;
ensure_eq!(
sender,
min_reserve_price_manager,
ContractError::Unauthorized {}
);
Ok(())
}

pub fn build_blacklist_key(collection: &str, token_id: &str) -> String {
format!("{}/{}", collection, token_id)
}

pub fn only_not_blacklisted(
storage: &dyn Storage,
collection: &Addr,
token_id: &str,
) -> Result<(), ContractError> {
let key = build_blacklist_key(collection.as_ref(), token_id);
if BLACKLIST.has(storage, key) {
return Err(ContractError::TokenBlacklisted(
collection.to_string(),
token_id.to_string(),
));
}
Ok(())
}

pub fn only_blacklist_manager(sender: &Addr) -> Result<(), ContractError> {
ensure_eq!(
sender.as_str(),
BLACKLIST_MANAGER,
ContractError::Unauthorized {}
);
Ok(())
}

pub fn validate_reserve_price(
storage: &dyn Storage,
check_reserve_price: &Coin,
Expand Down Expand Up @@ -59,6 +103,9 @@ pub fn settle_auction(
ContractError::AuctionNotEnded {}
);

// Check if token is blacklisted before settling
only_not_blacklisted(deps.storage, &auction.collection, &auction.token_id)?;

// If auction is set to end within a halt window, then postpone it instead
let auction_end_time = auction.end_time.unwrap();
if halt_manager.is_within_halt_window(auction_end_time.seconds()) {
Expand Down
8 changes: 7 additions & 1 deletion contracts/reserve-auction/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use cosmwasm_std::entry_point;

use crate::msg::InstantiateMsg;
use crate::state::{Config, HaltManager, HALT_MANAGER};
use crate::state::{Config, HaltManager, HALT_MANAGER, MIN_RESERVE_PRICE_MANAGER};
use crate::{error::ContractError, state::MIN_RESERVE_PRICES};
use cosmwasm_std::{DepsMut, Env, Event, MessageInfo};
use cw2::set_contract_version;
Expand All @@ -20,6 +20,12 @@ pub fn instantiate(
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

MIN_RESERVE_PRICE_MANAGER.save(
deps.storage,
&deps.api.addr_validate(&msg.min_reserve_price_manager)?,
)?;

let config = Config {
fair_burn: deps.api.addr_validate(&msg.fair_burn)?,
trading_fee_percent: msg.trading_fee_percent,
Expand Down
Loading