Skip to content

Commit 355347d

Browse files
authored
Escrow for marketplace (#69)
Added ability for the marketplace pallet to use Escrow pallet. Now Ask orders can accept optional escrow_agent parameter. If this parameter is present - the seller's funds will be reserved.
1 parent ff2892f commit 355347d

File tree

15 files changed

+332
-40
lines changed

15 files changed

+332
-40
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pallets/marketplace/Cargo.toml

+3-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ sp-std = { workspace = true }
3030
sp-io = { workspace = true }
3131
sp-runtime = { workspace = true, default-features = false }
3232
pallet-nfts = { workspace = true, default-features = false }
33-
sp-core = { workspace = true, default-features = false}
33+
sp-core = { workspace = true, default-features = false }
3434
sp-keystore = { workspace = true, default-features = false }
3535
pallet-balances = { workspace = true, default-features = false }
3636
pallet-timestamp = { workspace = true, default-features = false }
@@ -50,9 +50,7 @@ std = [
5050
"sp-io/std",
5151
"sp-std/std",
5252
"pallet-balances/std",
53-
"pallet-nfts/std",
54-
]
55-
runtime-benchmarks = [
56-
"frame-benchmarking/runtime-benchmarks",
53+
"pallet-nfts/std",
5754
]
55+
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
5856
try-runtime = ["frame-support/try-runtime"]

pallets/marketplace/src/benchmarking.rs

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pub mod benchmarks {
105105
expires_at: Timestamp::<T>::get() + T::BenchmarkHelper::timestamp(100000),
106106
price,
107107
fee: BalanceOf::<T>::from(0u8),
108+
escrow_agent: None,
108109
signature_data: SignatureData {
109110
signature: EthereumSignature::from(Signature::from_raw([0; 65])).into(),
110111
nonce: vec![0],
@@ -215,6 +216,7 @@ pub mod benchmarks {
215216
expires_at: Timestamp::<T>::get() + T::BenchmarkHelper::timestamp(100000),
216217
price,
217218
fee: BalanceOf::<T>::from(1u8),
219+
escrow_agent: None,
218220
signature_data: SignatureData {
219221
signature: EthereumSignature::from(Signature::from_raw([0; 65])).into(),
220222
nonce: vec![1],

pallets/marketplace/src/lib.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ pub mod pallet {
6363
/// The currency trait.
6464
type Currency: Inspect<Self::AccountId>
6565
+ Mutate<Self::AccountId>
66-
+ MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
66+
+ MutateHold<Self::AccountId, Reason = <Self as pallet::Config>::RuntimeHoldReason>;
67+
68+
type Escrow: Escrow<Self::AccountId, BalanceOf<Self>, Self::AccountId>;
6769

6870
/// Overarching hold reason.
6971
type RuntimeHoldReason: From<HoldReason>;
@@ -121,7 +123,7 @@ pub mod pallet {
121123
T::CollectionId,
122124
Blake2_128Concat,
123125
T::ItemId,
124-
Ask<T::AccountId, BalanceOf<T>, T::Moment>,
126+
Ask<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>,
125127
>;
126128

127129
#[pallet::storage]
@@ -382,6 +384,7 @@ pub mod pallet {
382384
order.item,
383385
&order.price,
384386
&order.fee,
387+
order.escrow_agent,
385388
)?;
386389
} else {
387390
ensure!(
@@ -394,6 +397,7 @@ pub mod pallet {
394397
price: order.price,
395398
expiration: order.expires_at,
396399
fee: order.fee,
400+
escrow_agent: order.escrow_agent,
397401
};
398402

399403
Asks::<T>::insert(order.collection, order.item, ask);
@@ -429,6 +433,7 @@ pub mod pallet {
429433
order.item,
430434
&order.price,
431435
&order.fee,
436+
order.escrow_agent,
432437
)?;
433438
} else {
434439
ensure!(
@@ -529,7 +534,7 @@ pub mod pallet {
529534
collection: &T::CollectionId,
530535
item: &T::ItemId,
531536
price: &BalanceOf<T>,
532-
) -> Option<ExecOrder<T::AccountId, BalanceOf<T>, T::Moment>> {
537+
) -> Option<ExecOrder<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>> {
533538
let timestamp = pallet_timestamp::Pallet::<T>::get();
534539

535540
match order_type {
@@ -554,17 +559,19 @@ pub mod pallet {
554559
}
555560

556561
pub fn execute_order(
557-
exec_order: ExecOrder<T::AccountId, BalanceOf<T>, T::Moment>,
562+
exec_order: ExecOrder<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>,
558563
who: T::AccountId,
559564
collection: T::CollectionId,
560565
item: T::ItemId,
561566
price: &BalanceOf<T>,
562567
fee: &BalanceOf<T>,
568+
order_escrow_agent: Option<T::AccountId>,
563569
) -> Result<(), DispatchError> {
564570
let seller: T::AccountId;
565571
let buyer: T::AccountId;
566572
let seller_fee: BalanceOf<T>;
567573
let buyer_fee: BalanceOf<T>;
574+
let escrow_agent: Option<T::AccountId>;
568575

569576
match exec_order {
570577
ExecOrder::Bid(bid) => {
@@ -574,6 +581,7 @@ pub mod pallet {
574581
buyer = bid.buyer;
575582
seller_fee = *fee;
576583
buyer_fee = bid.fee;
584+
escrow_agent = order_escrow_agent;
577585
},
578586
ExecOrder::Ask(ask) => {
579587
ensure!(who.clone() != ask.seller.clone(), Error::<T>::BuyerIsSeller);
@@ -582,13 +590,14 @@ pub mod pallet {
582590
buyer = who;
583591
seller_fee = ask.fee;
584592
buyer_fee = *fee;
593+
escrow_agent = ask.escrow_agent;
585594
},
586595
};
587596

588597
Asks::<T>::remove(collection, item);
589598
Bids::<T>::remove((collection, item, *price));
590599

591-
Self::process_fees(&seller, seller_fee, &buyer, buyer_fee, *price)?;
600+
Self::process_fees(&seller, seller_fee, &buyer, buyer_fee, *price, escrow_agent)?;
592601

593602
pallet_nfts::Pallet::<T>::enable_transfer(&collection, &item)?;
594603
<pallet_nfts::Pallet<T> as Transfer<T::AccountId>>::transfer(
@@ -622,6 +631,7 @@ pub mod pallet {
622631
buyer: &T::AccountId,
623632
buyer_fee: BalanceOf<T>,
624633
price: BalanceOf<T>,
634+
escrow_agent: Option<T::AccountId>,
625635
) -> Result<(), DispatchError> {
626636
//Amount to be payed by the buyer
627637
let buyer_payment_amount = price.checked_add(&buyer_fee).ok_or(Error::<T>::Overflow)?;
@@ -651,7 +661,19 @@ pub mod pallet {
651661
Preserve,
652662
)?;
653663
//Pay earnings to seller
654-
<T as crate::Config>::Currency::transfer(buyer, seller, seller_pay_amount, Preserve)?;
664+
match escrow_agent {
665+
Some(agent) => {
666+
T::Escrow::make_deposit(buyer, seller, seller_pay_amount, &agent)?;
667+
},
668+
None => {
669+
<T as crate::Config>::Currency::transfer(
670+
buyer,
671+
seller,
672+
seller_pay_amount,
673+
Preserve,
674+
)?;
675+
},
676+
}
655677

656678
Ok(())
657679
}

pallets/marketplace/src/mock.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use frame_support::{
2-
derive_impl, parameter_types,
3-
traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64},
2+
derive_impl,
3+
pallet_prelude::DispatchResult,
4+
parameter_types,
5+
traits::{
6+
fungible::Mutate, AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64,
7+
NamedReservableCurrency,
8+
},
49
};
510
use frame_system as system;
611
use sp_core::H256;
@@ -19,6 +24,8 @@ type Signature = EthereumSignature;
1924
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
2025
type Block = frame_system::mocking::MockBlock<Test>;
2126

27+
pub const ESCROW_RESERVE_NAME: &[u8; 8] = b"escrow__";
28+
2229
// Configure a mock runtime to test the pallet.
2330
frame_support::construct_runtime!(
2431
pub enum Test
@@ -97,10 +104,35 @@ impl pallet_nfts::Config for Test {
97104
}
98105
}
99106

107+
pub struct EscrowMock {
108+
pub deposit: u128,
109+
}
110+
111+
impl pallet_marketplace::Escrow<AccountId, u128, AccountId> for EscrowMock {
112+
fn make_deposit(
113+
depositor: &AccountId,
114+
destination: &AccountId,
115+
value: u128,
116+
_escrow_agent: &AccountId,
117+
) -> DispatchResult {
118+
Balances::transfer(
119+
depositor,
120+
destination,
121+
value,
122+
frame_support::traits::tokens::Preservation::Expendable,
123+
)?;
124+
125+
Balances::reserve_named(ESCROW_RESERVE_NAME, destination, value)?;
126+
127+
Ok(())
128+
}
129+
}
130+
100131
impl pallet_marketplace::Config for Test {
101132
type RuntimeEvent = RuntimeEvent;
102133
type RuntimeCall = RuntimeCall;
103134
type Currency = Balances;
135+
type Escrow = EscrowMock;
104136
type RuntimeHoldReason = RuntimeHoldReason;
105137
type MinOrderDuration = ConstU64<10>;
106138
type NonceStringLimit = ConstU32<50>;
@@ -114,7 +146,7 @@ impl pallet_marketplace::Config for Test {
114146

115147
impl pallet_balances::Config for Test {
116148
type MaxLocks = ();
117-
type MaxReserves = ();
149+
type MaxReserves = ConstU32<2>;
118150
type ReserveIdentifier = [u8; 8];
119151
type Balance = u128;
120152
type DustRemoval = ();

0 commit comments

Comments
 (0)