diff --git a/Cargo.lock b/Cargo.lock index 7df2383..29eb590 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,6 +186,7 @@ dependencies = [ name = "commitment-marketplace" version = "0.1.0" dependencies = [ + "shared_utils", "soroban-sdk", ] diff --git a/contracts/commitment_marketplace/Cargo.toml b/contracts/commitment_marketplace/Cargo.toml index 5f9a3f2..3918f37 100644 --- a/contracts/commitment_marketplace/Cargo.toml +++ b/contracts/commitment_marketplace/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["cdylib"] [dependencies] soroban-sdk = "21.0.0" +shared_utils = { path = "../shared_utils" } [dev-dependencies] soroban-sdk = { version = "21.0.0", features = ["testutils"] } diff --git a/contracts/commitment_marketplace/src/lib.rs b/contracts/commitment_marketplace/src/lib.rs index 3a5b0e6..3d6e875 100644 --- a/contracts/commitment_marketplace/src/lib.rs +++ b/contracts/commitment_marketplace/src/lib.rs @@ -25,6 +25,7 @@ use soroban_sdk::{ contract, contracterror, contractimpl, contracttype, symbol_short, token, Address, Env, Symbol, Vec, }; +use shared_utils::math::SafeMath; // ============================================================================ // Error Types @@ -608,9 +609,11 @@ impl CommitmentMarketplace { MarketplaceError::NotInitialized })?; - // Calculate fee and seller proceeds - let marketplace_fee = (listing.price * fee_basis_points as i128) / 10000; - let seller_proceeds = listing.price - marketplace_fee; + // Calculate fee and seller proceeds safely using basis points (bps) + let fee_basis_points_i128: i128 = fee_basis_points as i128; + let marketplace_fee = + SafeMath::div(SafeMath::mul(listing.price, fee_basis_points_i128), 10_000_i128); + let seller_proceeds = SafeMath::sub(listing.price, marketplace_fee); // EFFECTS // Remove listing first (prevent reentrancy) @@ -814,6 +817,13 @@ impl CommitmentMarketplace { // CHECKS seller.require_auth(); + if seller == offerer { + e.storage() + .instance() + .set(&DataKey::ReentrancyGuard, &false); + return Err(MarketplaceError::CannotBuyOwnListing); + } + let offers: Vec = e .storage() .persistent() @@ -1025,7 +1035,10 @@ impl CommitmentMarketplace { // EFFECTS let started_at = e.ledger().timestamp(); - let ends_at = started_at + duration_seconds; + let ends_at = started_at.checked_add(duration_seconds).ok_or_else(|| { + e.storage().instance().set(&DataKey::ReentrancyGuard, &false); + MarketplaceError::InvalidDuration + })?; let auction = Auction { token_id, @@ -1266,9 +1279,16 @@ impl CommitmentMarketplace { // INTERACTIONS if let Some(winner) = auction.highest_bidder { - // Calculate fees - let marketplace_fee = (auction.current_bid * fee_basis_points as i128) / 10000; - let seller_proceeds = auction.current_bid - marketplace_fee; + // Calculate fees safely using basis points (bps, /10_000) + let fee_bps = if fee_basis_points > 10_000 { + 10_000 + } else { + fee_basis_points + }; + let fee_bps_i128 = fee_bps as i128; + let marketplace_fee = + SafeMath::div(SafeMath::mul(auction.current_bid, fee_bps_i128), 10_000_i128); + let seller_proceeds = SafeMath::sub(auction.current_bid, marketplace_fee); let payment_token_client = token::Client::new(&e, &auction.payment_token); diff --git a/contracts/commitment_marketplace/src/tests.rs b/contracts/commitment_marketplace/src/tests.rs index d256189..818c474 100644 --- a/contracts/commitment_marketplace/src/tests.rs +++ b/contracts/commitment_marketplace/src/tests.rs @@ -323,6 +323,53 @@ fn test_make_duplicate_offer_fails() { client.make_offer(&offerer, &1, &600, &payment_token); // Should fail } +#[test] +#[should_panic(expected = "Error(Contract, #8)")] // CannotBuyOwnListing +fn test_make_offer_own_listing_fails() { + let e = Env::default(); + e.mock_all_auths(); + + let (_, _, client) = setup_marketplace(&e); + + let seller = Address::generate(&e); + let payment_token = setup_test_token(&e); + + client.list_nft(&seller, &1, &1000, &payment_token); + client.make_offer(&seller, &1, &800, &payment_token); // Seller making offer on own listing +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] // CannotBuyOwnListing +fn test_make_offer_own_auction_fails() { + let e = Env::default(); + e.mock_all_auths(); + + let (_, _, client) = setup_marketplace(&e); + + let seller = Address::generate(&e); + let payment_token = setup_test_token(&e); + + client.start_auction(&seller, &1, &1000, &86400, &payment_token); + client.make_offer(&seller, &1, &1100, &payment_token); // Seller making offer on own auction +} + +#[test] +#[should_panic(expected = "Error(Contract, #8)")] // CannotBuyOwnListing +fn test_accept_offer_own_listing_fails() { + let e = Env::default(); + e.mock_all_auths(); + + let (_, _, client) = setup_marketplace(&e); + + let seller = Address::generate(&e); + let payment_token = setup_test_token(&e); + + client.make_offer(&seller, &1, &1000, &payment_token); + client.accept_offer(&seller, &1, &seller); // Seller accepting own offer +} + + + #[test] fn test_multiple_offers_same_token() { let e = Env::default();