Skip to content

Commit a14fe72

Browse files
committed
fix: remove default features for asset-base
1 parent d07ff1e commit a14fe72

File tree

8 files changed

+271
-70
lines changed

8 files changed

+271
-70
lines changed

contracts/asset/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ serde = { workspace = true }
1919
thiserror = { workspace = true }
2020

2121
[features]
22-
default = ["asset_base"]
22+
default = []
2323
library = []
2424
asset_base = []
2525
crossmint = []

contracts/asset/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@ pub enum ContractError {
2222
#[error("Invalid payment: {price} {denom}")]
2323
InvalidPayment { price: u128, denom: String },
2424

25+
#[error("Insufficient funds")]
26+
InsufficientFunds {},
27+
2528
#[error("No payment")]
2629
NoPayment {},
2730

2831
#[error("Plugin error: {msg}")]
2932
PluginError { msg: String },
33+
34+
#[error("Invalid marketplace fee: {bps}, {recipient}")]
35+
InvalidMarketplaceFee { bps: u16, recipient: String },
36+
3037
}
3138

3239
impl From<ContractError> for cw721::error::Cw721ContractError {

contracts/asset/src/execute.rs

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use cosmwasm_std::{
2-
BankMsg, Coin, CosmosMsg, CustomMsg, Deps, DepsMut, Env, MessageInfo, Response, StdError
2+
Addr, BankMsg, Coin, CosmosMsg, CustomMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdError
33
};
44
use cw721::{state::NftInfo, traits::Cw721State, Expiration};
55

@@ -15,6 +15,8 @@ pub fn list<TNftExtension, TCustomResponseMsg>(
1515
id: String,
1616
price: Coin,
1717
reservation: Option<Reserve>,
18+
marketplace_fee_bps: Option<u16>,
19+
marketplace_fee_recipient: Option<String>,
1820
) -> Result<Response<TCustomResponseMsg>, ContractError>
1921
where
2022
TNftExtension: Cw721State,
@@ -31,6 +33,25 @@ where
3133
price: price.amount.u128(),
3234
});
3335
}
36+
37+
// validate market place fee and recipient
38+
if let Some(market_fee) = marketplace_fee_bps {
39+
if market_fee > 10_000 {
40+
return Err(ContractError::InvalidMarketplaceFee {
41+
bps: market_fee,
42+
recipient: marketplace_fee_recipient.clone().unwrap_or_default(),
43+
});
44+
}
45+
}
46+
let (marketplace_fee_bps_ref, marketplace_fee_recipient_ref) = (&marketplace_fee_bps, &marketplace_fee_recipient);
47+
if marketplace_fee_bps_ref.is_some() && marketplace_fee_recipient_ref.is_none() {
48+
return Err(ContractError::InvalidMarketplaceFee {
49+
bps: marketplace_fee_bps_ref.unwrap(),
50+
recipient: "".to_string(),
51+
});
52+
} else if marketplace_fee_bps_ref.is_some() && marketplace_fee_recipient_ref.is_some() {
53+
deps.api.addr_validate(marketplace_fee_recipient_ref.as_ref().unwrap())?;
54+
}
3455
// Ensure the listing does not already exist
3556
let old_listing = asset_config.listings.may_load(deps.storage, &id)?;
3657
if old_listing.is_some() {
@@ -43,6 +64,8 @@ where
4364
price: price.clone(),
4465
reserved: reservation.clone(),
4566
nft_info,
67+
marketplace_fee_bps,
68+
marketplace_fee_recipient: Some(deps.api.addr_validate(&marketplace_fee_recipient.unwrap())?),
4669
};
4770
asset_config.listings.save(deps.storage, &id, &listing)?;
4871
Ok(Response::default()
@@ -137,6 +160,7 @@ pub fn buy<TNftExtension, TCustomResponseMsg>(
137160
info: &MessageInfo,
138161
id: String,
139162
recipient: Option<String>,
163+
deductions: Vec<(String, Coin, String)>,
140164
) -> Result<Response<TCustomResponseMsg>, ContractError>
141165
where
142166
TNftExtension: Cw721State,
@@ -152,11 +176,11 @@ where
152176
let price = listing.price.clone();
153177
let seller = listing.seller.clone();
154178

155-
let payment = info
179+
let mut payment = info
156180
.funds
157181
.iter()
158182
.find(|coin| coin.denom == price.denom)
159-
.ok_or_else(|| ContractError::NoPayment {})?;
183+
.ok_or_else(|| ContractError::NoPayment {})?.clone();
160184

161185
if payment.amount.lt(&price.amount) || payment.denom != price.denom {
162186
return Err(ContractError::InvalidPayment {
@@ -165,6 +189,30 @@ where
165189
});
166190
}
167191

192+
let mut response = Response::<TCustomResponseMsg>::default();
193+
194+
if let Some(market_fee) = listing.marketplace_fee_bps {
195+
let fee_amount = payment.amount.checked_multiply_ratio(market_fee, 10_000 as u128).map_err(|_| ContractError::InsufficientFunds {})?;
196+
payment.amount = payment.amount.checked_sub(fee_amount).map_err(|_| ContractError::InsufficientFunds {})?;
197+
if let Some(recipient) = listing.marketplace_fee_recipient {
198+
if !fee_amount.is_zero() {
199+
response = response.add_attribute("marketplace_fee", fee_amount.to_string());
200+
response = response.add_message(BankMsg::Send {
201+
to_address: recipient.to_string(),
202+
amount: vec![Coin {
203+
denom: payment.denom.clone(),
204+
amount: fee_amount,
205+
}],
206+
});
207+
}
208+
}
209+
}
210+
211+
// remove all other deductions e.g. royalties from payment
212+
for (_, amount, _) in deductions {
213+
payment.amount = payment.amount.checked_sub(amount.amount).map_err(|_| ContractError::InsufficientFunds {})?;
214+
}
215+
168216
let buyer = match recipient {
169217
Some(addr) => deps.api.addr_validate(&addr)?,
170218
None => info.sender.clone(),
@@ -187,17 +235,18 @@ where
187235

188236
asset_config.listings.remove(deps.storage, &id)?;
189237

190-
Ok(Response::default()
238+
response = response
191239
.add_message(BankMsg::Send {
192240
to_address: seller.to_string(),
193-
amount: vec![payment.clone()], // we send the entire payment to the seller
241+
amount: vec![payment.clone()], // we send the remaining payment after deductions to the seller
194242
})
195243
.add_attribute("action", "buy")
196244
.add_attribute("id", id)
197245
.add_attribute("price", price.amount.to_string())
198246
.add_attribute("denom", price.denom)
199247
.add_attribute("seller", seller.to_string())
200-
.add_attribute("buyer", buyer.to_string()))
248+
.add_attribute("buyer", buyer.to_string());
249+
Ok(response)
201250
}
202251

203252
/// returns true if the sender can list the token
@@ -275,6 +324,8 @@ fn test_list() {
275324
"token-1".to_string(),
276325
price.clone(),
277326
None,
327+
None,
328+
None
278329
)
279330
.unwrap();
280331

@@ -312,6 +363,8 @@ fn test_list() {
312363
"token-1".to_string(),
313364
Coin::new(200 as u128, "uxion"),
314365
None,
366+
None,
367+
None
315368
)
316369
.unwrap_err();
317370
assert_eq!(
@@ -346,6 +399,8 @@ fn test_list() {
346399
"token-2".to_string(),
347400
Coin::new(100 as u128, "uxion"),
348401
None,
402+
None,
403+
None
349404
)
350405
.unwrap_err();
351406
assert_eq!(err, ContractError::Unauthorized {});
@@ -380,6 +435,8 @@ fn test_list() {
380435
"token-3".to_string(),
381436
price.clone(),
382437
None,
438+
None,
439+
None
383440
)
384441
.unwrap();
385442

@@ -447,6 +504,8 @@ fn test_list() {
447504
"token-4".to_string(),
448505
price.clone(),
449506
None,
507+
None,
508+
None
450509
)
451510
.unwrap();
452511

@@ -516,6 +575,8 @@ fn test_list() {
516575
"token-5".to_string(),
517576
Coin::new(100 as u128, "uxion"),
518577
None,
578+
None,
579+
None
519580
)
520581
.unwrap_err();
521582
assert_eq!(err, ContractError::Unauthorized {});
@@ -545,6 +606,8 @@ fn test_list() {
545606
"token-3".to_string(),
546607
Coin::new(0 as u128, "uxion"),
547608
None,
609+
None,
610+
None
548611
)
549612
.unwrap_err();
550613
assert_eq!(err, ContractError::InvalidListingPrice { price: 0 });
@@ -562,6 +625,8 @@ fn test_list() {
562625
"token-999".to_string(),
563626
Coin::new(100 as u128, "uxion"),
564627
None,
628+
None,
629+
None
565630
)
566631
.unwrap_err();
567632
match err {
@@ -605,6 +670,8 @@ fn test_buy() {
605670
price: price.clone(),
606671
reserved: None,
607672
nft_info: nft_info.clone(),
673+
marketplace_fee_bps: Some(100),
674+
marketplace_fee_recipient: Some(seller_addr.clone()),
608675
},
609676
)
610677
.unwrap();
@@ -615,6 +682,7 @@ fn test_buy() {
615682
&message_info(&buyer_addr, &[price.clone()]),
616683
"token-1".to_string(),
617684
None,
685+
vec![(seller_addr.to_string(), coin(10 as u128, "uxion"), "marketplace_fee".to_string())],
618686
)
619687
.unwrap();
620688

@@ -626,6 +694,7 @@ fn test_buy() {
626694
assert_eq!(
627695
attrs,
628696
vec![
697+
("marketplace_fee".to_string(), "100".to_string()),
629698
("action".to_string(), "buy".to_string()),
630699
("id".to_string(), "token-1".to_string()),
631700
("price".to_string(), price.amount.to_string()),
@@ -643,7 +712,10 @@ fn test_buy() {
643712
msgs,
644713
vec![CosmosMsg::Bank(BankMsg::Send {
645714
to_address: seller_addr.to_string(),
646-
amount: coins(100 as u128, "uxion"),
715+
amount: coins(1 as u128, "uxion"),
716+
}),CosmosMsg::Bank(BankMsg::Send {
717+
to_address: seller_addr.to_string(),
718+
amount: coins(99 as u128, "uxion"),
647719
})],
648720
);
649721

@@ -691,6 +763,8 @@ fn test_buy() {
691763
price: price.clone(),
692764
reserved: None,
693765
nft_info: nft_info.clone(),
766+
marketplace_fee_bps: None,
767+
marketplace_fee_recipient: None,
694768
},
695769
)
696770
.unwrap();
@@ -701,6 +775,7 @@ fn test_buy() {
701775
&message_info(&buyer_addr, &[coin(50 as u128, "uxion")]),
702776
"token-2".to_string(),
703777
None,
778+
vec![],
704779
)
705780
.unwrap_err();
706781
assert_eq!(
@@ -722,6 +797,7 @@ fn test_buy() {
722797
&message_info(&buyer_addr, &[coin(100 as u128, "uxion")]),
723798
"token-3".to_string(),
724799
None,
800+
vec![],
725801
)
726802
.unwrap_err();
727803
assert_eq!(
@@ -763,6 +839,8 @@ fn test_buy() {
763839
reserved_until: Expiration::AtHeight(env.block.height + 100),
764840
}),
765841
nft_info: nft_info.clone(),
842+
marketplace_fee_bps: None,
843+
marketplace_fee_recipient: None,
766844
},
767845
)
768846
.unwrap();
@@ -780,6 +858,8 @@ fn test_buy() {
780858
reserved_until: Expiration::AtHeight(env.block.height + 100),
781859
}),
782860
nft_info: nft_info.clone(),
861+
marketplace_fee_bps: None,
862+
marketplace_fee_recipient: None,
783863
},
784864
)
785865
.unwrap();
@@ -790,6 +870,7 @@ fn test_buy() {
790870
&message_info(&seller_addr, &[price.clone()]),
791871
"token-4".to_string(),
792872
None,
873+
vec![],
793874
)
794875
.unwrap_err();
795876
assert_eq!(err, ContractError::Unauthorized {});
@@ -801,6 +882,7 @@ fn test_buy() {
801882
&message_info(&buyer_addr, &[price.clone()]),
802883
"token-4".to_string(),
803884
None,
885+
vec![],
804886
)
805887
.unwrap();
806888
let attrs: Vec<(String, String)> = response
@@ -834,6 +916,8 @@ fn test_buy() {
834916
reserved_until: Expiration::AtHeight(env.block.height + 100),
835917
}),
836918
nft_info: nft_info.clone(),
919+
marketplace_fee_bps: None,
920+
marketplace_fee_recipient: None,
837921
},
838922
)
839923
.unwrap();
@@ -844,6 +928,7 @@ fn test_buy() {
844928
&message_info(&seller_addr, &[price.clone()]),
845929
"token-4".to_string(),
846930
Some(buyer_addr.to_string()), // buyer is the reserver
931+
vec![],
847932
)
848933
.unwrap();
849934
let attrs: Vec<(String, String)> = response
@@ -898,6 +983,8 @@ fn test_delist() {
898983
price: price.clone(),
899984
reserved: None,
900985
nft_info: nft_info.clone(),
986+
marketplace_fee_bps: None,
987+
marketplace_fee_recipient: None,
901988
},
902989
)
903990
.unwrap();
@@ -963,6 +1050,8 @@ fn test_delist() {
9631050
price: price.clone(),
9641051
reserved: None,
9651052
nft_info: nft_info.clone(),
1053+
marketplace_fee_bps: None,
1054+
marketplace_fee_recipient: None,
9661055
},
9671056
)
9681057
.unwrap();
@@ -1025,6 +1114,8 @@ fn test_delist() {
10251114
reserver: seller_addr.clone(),
10261115
}),
10271116
nft_info: nft_info.clone(),
1117+
marketplace_fee_bps: None,
1118+
marketplace_fee_recipient: None,
10281119
},
10291120
)
10301121
.unwrap();
@@ -1100,6 +1191,8 @@ fn test_reserve() {
11001191
price: price.clone(),
11011192
reserved: None,
11021193
nft_info: nft_info.clone(),
1194+
marketplace_fee_bps: None,
1195+
marketplace_fee_recipient: None,
11031196
},
11041197
)
11051198
.unwrap();

contracts/asset/src/msg.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub enum AssetExtensionExecuteMsg {
1111
token_id: String,
1212
price: Coin,
1313
reservation: Option<Reserve>,
14+
marketplace_fee_bps: Option<u16>,
15+
marketplace_fee_recipient: Option<String>,
1416
},
1517
Reserve {
1618
token_id: String,

0 commit comments

Comments
 (0)