Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow buy nows to use the denom of the asking price #335

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
23 changes: 15 additions & 8 deletions contracts/stargaze-marketplace-v2/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ pub fn execute_set_ask(
only_tradable(&deps.querier, &env.block, &collection)?;

let config = CONFIG.load(deps.storage)?;
only_valid_price(deps.storage, &config, &collection, &details.price)?;
only_valid_price(deps.storage, &config, &collection, &details.price, None)?;

// Check and collect listing fee
let listing_payment = one_coin(&info)?;
Expand Down Expand Up @@ -349,7 +349,7 @@ pub fn execute_update_ask(
)
);

only_valid_price(deps.storage, &config, &ask.collection, &details.price)?;
only_valid_price(deps.storage, &config, &ask.collection, &details.price, None)?;

ask.details = details;

Expand Down Expand Up @@ -501,7 +501,6 @@ pub fn execute_set_bid(
only_tradable(&deps.querier, &env.block, &collection)?;

let config = CONFIG.load(deps.storage)?;
only_valid_price(deps.storage, &config, &collection, &details.price)?;

let mut funds = NativeBalance(info.funds.clone());
funds.normalize();
Expand All @@ -511,9 +510,9 @@ pub fn execute_set_bid(

let bid = Bid::new(
info.sender.clone(),
collection,
collection.clone(),
token_id,
details,
details.clone(),
env.block.height,
nonce,
);
Expand All @@ -524,6 +523,13 @@ pub fn execute_set_bid(

// bid could have a match with an existing ask without the need of buy_now
if let Some(ask) = matching_ask {
only_valid_price(
deps.storage,
&config,
&collection,
&details.price,
Some(&ask.details.price.denom),
)?;
// If a matching ask is found perform the sale
funds = funds
.sub(ask.details.price.clone())
Expand Down Expand Up @@ -552,7 +558,7 @@ pub fn execute_set_bid(
// If no match is found. Bid creation should:
// * store the bid
// * emit event

only_valid_price(deps.storage, &config, &collection, &details.price, None)?;
funds = funds
.sub(bid.details.price.clone())
.map_err(|_| ContractError::InsufficientFunds)?;
Expand Down Expand Up @@ -609,7 +615,7 @@ pub fn execute_update_bid(
)
);

only_valid_price(deps.storage, &config, &bid.collection, &details.price)?;
only_valid_price(deps.storage, &config, &bid.collection, &details.price, None)?;

let mut funds = NativeBalance(info.funds.clone());
funds.normalize();
Expand Down Expand Up @@ -775,7 +781,7 @@ pub fn execute_set_collection_bid(
only_tradable(&deps.querier, &env.block, &collection)?;

let config = CONFIG.load(deps.storage)?;
only_valid_price(deps.storage, &config, &collection, &details.price)?;
only_valid_price(deps.storage, &config, &collection, &details.price, None)?;

let mut funds = NativeBalance(info.funds.clone());
funds.normalize();
Expand Down Expand Up @@ -883,6 +889,7 @@ pub fn execute_update_collection_bid(
&config,
&collection_bid.collection,
&details.price,
None,
)?;

let mut funds = NativeBalance(info.funds.clone());
Expand Down
24 changes: 17 additions & 7 deletions contracts/stargaze-marketplace-v2/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,34 @@
Ok(())
}

// only_valid_price checks non zero amounts and enforces being in the collection denom or optional denom
pub fn only_valid_price(
storage: &dyn Storage,
config: &Config<Addr>,
collection: &Addr,
price: &Coin,
denom: Option<&str>,
) -> Result<(), ContractError> {
ensure!(
price.amount > Uint128::zero(),
ContractError::InvalidInput("order price must be greater than 0".to_string())
);

let query_result = COLLECTION_DENOMS.may_load(storage, collection.clone())?;
let collection_denom = query_result.unwrap_or(config.default_denom.clone());
ensure_eq!(
collection_denom,
price.denom,
ContractError::InvalidInput("invalid denom".to_string())
);
if let Some(denom) = denom {
ensure_eq!(
denom,
price.denom,
ContractError::InvalidInput("invalid denom".to_string())

Check warning on line 72 in contracts/stargaze-marketplace-v2/src/helpers.rs

View check run for this annotation

Codecov / codecov/patch

contracts/stargaze-marketplace-v2/src/helpers.rs#L72

Added line #L72 was not covered by tests
);
} else {
let query_result = COLLECTION_DENOMS.may_load(storage, collection.clone())?;
let collection_denom = query_result.unwrap_or(config.default_denom.clone());
ensure_eq!(
collection_denom,
price.denom,
ContractError::InvalidInput("invalid denom".to_string())
);
}

Ok(())
}
Expand Down
232 changes: 232 additions & 0 deletions contracts/stargaze-marketplace-v2/src/tests/unit_tests/sales.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,3 +845,235 @@ fn try_accept_ask_invalid_inputs() {
ContractError::InvalidInput("ask price is greater than max input".to_string()).to_string(),
);
}

#[test]
fn try_set_ask_with_old_denom() {
let TestContext {
mut app,
contracts:
TestContracts {
marketplace,
collection,
..
},
accounts:
TestAccounts {
creator,
owner,
bidder,
..
},
} = test_context();

let token_id = "1";
mint(&mut app, &creator, &owner, &collection, token_id);
approve(&mut app, &owner, &collection, &marketplace, token_id);

// Set ask with NATIVE_DENOM
let ask_price = coin(5_000_000, NATIVE_DENOM);
let set_ask = ExecuteMsg::SetAsk {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: ask_price.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
owner.clone(),
marketplace.clone(),
&set_ask,
&[coin(LISTING_FEE, NATIVE_DENOM)],
);
assert!(response.is_ok());

// Update collection denom to ATOM_DENOM
let update_collection_denom = ExecuteMsg::UpdateCollectionDenom {
collection: collection.to_string(),
denom: ATOM_DENOM.to_string(),
};
let response = app.execute_contract(
creator.clone(),
marketplace.clone(),
&update_collection_denom,
&[],
);
assert!(response.is_ok());
let atom_bid = coin(5_000_000, ATOM_DENOM);

// buying now should fail with ATOM_DENOM because listing is in NATIVE_DENOM
let buy_now = ExecuteMsg::BuySpecificNft {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: atom_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
bidder.clone(),
marketplace.clone(),
&buy_now,
&[atom_bid.clone()],
);
assert!(response.is_err());

// set bid with ATOM_DENOM should succeed
let set_bid = ExecuteMsg::SetBid {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: atom_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(bidder.clone(), marketplace.clone(), &set_bid, &[atom_bid]);
assert!(response.is_ok());

// set bid with NATIVE_DENOM should fail
let native_bid = coin(4_20, NATIVE_DENOM);
let set_bid_native = ExecuteMsg::SetBid {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: native_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
bidder.clone(),
marketplace.clone(),
&set_bid_native,
&[native_bid],
);
assert!(response.is_err());

let buy_price = coin(5_000_000, NATIVE_DENOM);
let buy_now_asking_price = ExecuteMsg::BuySpecificNft {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: buy_price.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
bidder,
marketplace.clone(),
&buy_now_asking_price,
&[buy_price],
);
assert!(response.is_ok());
}

#[test]
fn try_match_bid_with_old_denom() {
let TestContext {
mut app,
contracts:
TestContracts {
marketplace,
collection,
..
},
accounts:
TestAccounts {
creator,
owner,
bidder,
..
},
} = test_context();

let token_id = "1";
mint(&mut app, &creator, &owner, &collection, token_id);
approve(&mut app, &owner, &collection, &marketplace, token_id);

// Set ask with NATIVE_DENOM
let ask_price = coin(5_000_000, NATIVE_DENOM);
let set_ask = ExecuteMsg::SetAsk {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: ask_price.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
owner.clone(),
marketplace.clone(),
&set_ask,
&[coin(LISTING_FEE, NATIVE_DENOM)],
);
assert!(response.is_ok());

// Update collection denom to ATOM_DENOM
let update_collection_denom = ExecuteMsg::UpdateCollectionDenom {
collection: collection.to_string(),
denom: ATOM_DENOM.to_string(),
};
let response = app.execute_contract(
creator.clone(),
marketplace.clone(),
&update_collection_denom,
&[],
);
assert!(response.is_ok());
let atom_bid = coin(5_000_000, ATOM_DENOM);

// buying now should fail with ATOM_DENOM because listing is in NATIVE_DENOM
let buy_now = ExecuteMsg::BuySpecificNft {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: atom_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
bidder.clone(),
marketplace.clone(),
&buy_now,
&[atom_bid.clone()],
);
assert!(response.is_err());

// set bid with ATOM_DENOM should succeed
let set_bid = ExecuteMsg::SetBid {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: atom_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(bidder.clone(), marketplace.clone(), &set_bid, &[atom_bid]);
assert!(response.is_ok());

// set bid with NATIVE_DENOM suceed if it matches the ask price
let native_bid = coin(5_000_000, NATIVE_DENOM);
let set_bid_native = ExecuteMsg::SetBid {
collection: collection.to_string(),
token_id: token_id.to_string(),
details: OrderDetails {
price: native_bid.clone(),
recipient: None,
finder: None,
},
};
let response = app.execute_contract(
bidder.clone(),
marketplace.clone(),
&set_bid_native,
&[native_bid],
);
assert!(response.is_ok());
}
Loading