Skip to content

Commit

Permalink
Escrow for marketplace (#69)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nbokovoy authored May 14, 2024
1 parent ff2892f commit 355347d
Show file tree
Hide file tree
Showing 15 changed files with 332 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions pallets/marketplace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ sp-std = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true, default-features = false }
pallet-nfts = { workspace = true, default-features = false }
sp-core = { workspace = true, default-features = false}
sp-core = { workspace = true, default-features = false }
sp-keystore = { workspace = true, default-features = false }
pallet-balances = { workspace = true, default-features = false }
pallet-timestamp = { workspace = true, default-features = false }
Expand All @@ -50,9 +50,7 @@ std = [
"sp-io/std",
"sp-std/std",
"pallet-balances/std",
"pallet-nfts/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"pallet-nfts/std",
]
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
try-runtime = ["frame-support/try-runtime"]
2 changes: 2 additions & 0 deletions pallets/marketplace/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub mod benchmarks {
expires_at: Timestamp::<T>::get() + T::BenchmarkHelper::timestamp(100000),
price,
fee: BalanceOf::<T>::from(0u8),
escrow_agent: None,
signature_data: SignatureData {
signature: EthereumSignature::from(Signature::from_raw([0; 65])).into(),
nonce: vec![0],
Expand Down Expand Up @@ -215,6 +216,7 @@ pub mod benchmarks {
expires_at: Timestamp::<T>::get() + T::BenchmarkHelper::timestamp(100000),
price,
fee: BalanceOf::<T>::from(1u8),
escrow_agent: None,
signature_data: SignatureData {
signature: EthereumSignature::from(Signature::from_raw([0; 65])).into(),
nonce: vec![1],
Expand Down
34 changes: 28 additions & 6 deletions pallets/marketplace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ pub mod pallet {
/// The currency trait.
type Currency: Inspect<Self::AccountId>
+ Mutate<Self::AccountId>
+ MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
+ MutateHold<Self::AccountId, Reason = <Self as pallet::Config>::RuntimeHoldReason>;

type Escrow: Escrow<Self::AccountId, BalanceOf<Self>, Self::AccountId>;

/// Overarching hold reason.
type RuntimeHoldReason: From<HoldReason>;
Expand Down Expand Up @@ -121,7 +123,7 @@ pub mod pallet {
T::CollectionId,
Blake2_128Concat,
T::ItemId,
Ask<T::AccountId, BalanceOf<T>, T::Moment>,
Ask<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>,
>;

#[pallet::storage]
Expand Down Expand Up @@ -382,6 +384,7 @@ pub mod pallet {
order.item,
&order.price,
&order.fee,
order.escrow_agent,
)?;
} else {
ensure!(
Expand All @@ -394,6 +397,7 @@ pub mod pallet {
price: order.price,
expiration: order.expires_at,
fee: order.fee,
escrow_agent: order.escrow_agent,
};

Asks::<T>::insert(order.collection, order.item, ask);
Expand Down Expand Up @@ -429,6 +433,7 @@ pub mod pallet {
order.item,
&order.price,
&order.fee,
order.escrow_agent,
)?;
} else {
ensure!(
Expand Down Expand Up @@ -529,7 +534,7 @@ pub mod pallet {
collection: &T::CollectionId,
item: &T::ItemId,
price: &BalanceOf<T>,
) -> Option<ExecOrder<T::AccountId, BalanceOf<T>, T::Moment>> {
) -> Option<ExecOrder<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>> {
let timestamp = pallet_timestamp::Pallet::<T>::get();

match order_type {
Expand All @@ -554,17 +559,19 @@ pub mod pallet {
}

pub fn execute_order(
exec_order: ExecOrder<T::AccountId, BalanceOf<T>, T::Moment>,
exec_order: ExecOrder<T::AccountId, BalanceOf<T>, T::Moment, T::AccountId>,
who: T::AccountId,
collection: T::CollectionId,
item: T::ItemId,
price: &BalanceOf<T>,
fee: &BalanceOf<T>,
order_escrow_agent: Option<T::AccountId>,
) -> Result<(), DispatchError> {
let seller: T::AccountId;
let buyer: T::AccountId;
let seller_fee: BalanceOf<T>;
let buyer_fee: BalanceOf<T>;
let escrow_agent: Option<T::AccountId>;

match exec_order {
ExecOrder::Bid(bid) => {
Expand All @@ -574,6 +581,7 @@ pub mod pallet {
buyer = bid.buyer;
seller_fee = *fee;
buyer_fee = bid.fee;
escrow_agent = order_escrow_agent;
},
ExecOrder::Ask(ask) => {
ensure!(who.clone() != ask.seller.clone(), Error::<T>::BuyerIsSeller);
Expand All @@ -582,13 +590,14 @@ pub mod pallet {
buyer = who;
seller_fee = ask.fee;
buyer_fee = *fee;
escrow_agent = ask.escrow_agent;
},
};

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

Self::process_fees(&seller, seller_fee, &buyer, buyer_fee, *price)?;
Self::process_fees(&seller, seller_fee, &buyer, buyer_fee, *price, escrow_agent)?;

pallet_nfts::Pallet::<T>::enable_transfer(&collection, &item)?;
<pallet_nfts::Pallet<T> as Transfer<T::AccountId>>::transfer(
Expand Down Expand Up @@ -622,6 +631,7 @@ pub mod pallet {
buyer: &T::AccountId,
buyer_fee: BalanceOf<T>,
price: BalanceOf<T>,
escrow_agent: Option<T::AccountId>,
) -> Result<(), DispatchError> {
//Amount to be payed by the buyer
let buyer_payment_amount = price.checked_add(&buyer_fee).ok_or(Error::<T>::Overflow)?;
Expand Down Expand Up @@ -651,7 +661,19 @@ pub mod pallet {
Preserve,
)?;
//Pay earnings to seller
<T as crate::Config>::Currency::transfer(buyer, seller, seller_pay_amount, Preserve)?;
match escrow_agent {
Some(agent) => {
T::Escrow::make_deposit(buyer, seller, seller_pay_amount, &agent)?;
},
None => {
<T as crate::Config>::Currency::transfer(
buyer,
seller,
seller_pay_amount,
Preserve,
)?;
},
}

Ok(())
}
Expand Down
38 changes: 35 additions & 3 deletions pallets/marketplace/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use frame_support::{
derive_impl, parameter_types,
traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64},
derive_impl,
pallet_prelude::DispatchResult,
parameter_types,
traits::{
fungible::Mutate, AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64,
NamedReservableCurrency,
},
};
use frame_system as system;
use sp_core::H256;
Expand All @@ -19,6 +24,8 @@ type Signature = EthereumSignature;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
type Block = frame_system::mocking::MockBlock<Test>;

pub const ESCROW_RESERVE_NAME: &[u8; 8] = b"escrow__";

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test
Expand Down Expand Up @@ -97,10 +104,35 @@ impl pallet_nfts::Config for Test {
}
}

pub struct EscrowMock {
pub deposit: u128,
}

impl pallet_marketplace::Escrow<AccountId, u128, AccountId> for EscrowMock {
fn make_deposit(
depositor: &AccountId,
destination: &AccountId,
value: u128,
_escrow_agent: &AccountId,
) -> DispatchResult {
Balances::transfer(
depositor,
destination,
value,
frame_support::traits::tokens::Preservation::Expendable,
)?;

Balances::reserve_named(ESCROW_RESERVE_NAME, destination, value)?;

Ok(())
}
}

impl pallet_marketplace::Config for Test {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type Escrow = EscrowMock;
type RuntimeHoldReason = RuntimeHoldReason;
type MinOrderDuration = ConstU64<10>;
type NonceStringLimit = ConstU32<50>;
Expand All @@ -114,7 +146,7 @@ impl pallet_marketplace::Config for Test {

impl pallet_balances::Config for Test {
type MaxLocks = ();
type MaxReserves = ();
type MaxReserves = ConstU32<2>;
type ReserveIdentifier = [u8; 8];
type Balance = u128;
type DustRemoval = ();
Expand Down
Loading

0 comments on commit 355347d

Please sign in to comment.