-
Notifications
You must be signed in to change notification settings - Fork 2.6k
[Uniques V2] Atomic NFTs swap #12285
Changes from 27 commits
3086c76
12b756f
8083eca
5575d3f
5564a52
fc1024a
a2955be
bdc0538
a28cbcc
504b7d5
1ccf996
ca2d1ec
78f4570
7c15814
6232aa4
9acfb5c
8d6cbda
ee88eaa
2ce1ea3
99d1eb5
b404705
cccebd3
2202f0d
2688fe5
2dd02af
e565989
82841fc
7fbf339
e956594
b52a615
3b41ee1
239bf17
d086e81
c2afaa4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// This file is part of Substrate. | ||
|
||
// Copyright (C) 2022 Parity Technologies (UK) Ltd. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use crate::*; | ||
use frame_support::{ | ||
pallet_prelude::*, | ||
traits::{Currency, ExistenceRequirement::KeepAlive}, | ||
}; | ||
|
||
impl<T: Config<I>, I: 'static> Pallet<T, I> { | ||
pub fn do_create_swap( | ||
gilescope marked this conversation as resolved.
Show resolved
Hide resolved
|
||
caller: T::AccountId, | ||
offered_collection_id: T::CollectionId, | ||
offered_item_id: T::ItemId, | ||
desired_collection_id: T::CollectionId, | ||
maybe_desired_item_id: Option<T::ItemId>, | ||
maybe_price: Option<PriceWithDirection<ItemPrice<T, I>>>, | ||
duration: <T as SystemConfig>::BlockNumber, | ||
) -> DispatchResult { | ||
ensure!(duration <= T::MaxDeadlineDuration::get(), Error::<T, I>::WrongDuration); | ||
|
||
let item = Item::<T, I>::get(&offered_collection_id, &offered_item_id) | ||
.ok_or(Error::<T, I>::UnknownItem)?; | ||
ensure!(item.owner == caller, Error::<T, I>::NoPermission); | ||
|
||
match maybe_desired_item_id { | ||
Some(desired_item_id) => ensure!( | ||
Item::<T, I>::contains_key(&desired_collection_id, &desired_item_id), | ||
Error::<T, I>::UnknownItem | ||
), | ||
None => ensure!( | ||
Collection::<T, I>::contains_key(&desired_collection_id), | ||
Error::<T, I>::UnknownCollection | ||
), | ||
}; | ||
|
||
let now = frame_system::Pallet::<T>::block_number(); | ||
let deadline = duration.saturating_add(now); | ||
|
||
PendingSwapOf::<T, I>::insert( | ||
ggwpez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
&offered_collection_id, | ||
&offered_item_id, | ||
PendingSwap { | ||
desired_collection: desired_collection_id, | ||
desired_item: maybe_desired_item_id, | ||
price: maybe_price.clone(), | ||
deadline: deadline.clone(), | ||
}, | ||
); | ||
|
||
Self::deposit_event(Event::SwapCreated { | ||
offered_collection: offered_collection_id, | ||
offered_item: offered_item_id, | ||
desired_collection: desired_collection_id, | ||
desired_item: maybe_desired_item_id, | ||
price: maybe_price, | ||
deadline, | ||
}); | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn do_cancel_swap( | ||
caller: T::AccountId, | ||
offered_collection_id: T::CollectionId, | ||
offered_item_id: T::ItemId, | ||
) -> DispatchResult { | ||
let swap = PendingSwapOf::<T, I>::get(&offered_collection_id, &offered_item_id) | ||
.ok_or(Error::<T, I>::UnknownSwap)?; | ||
|
||
let now = frame_system::Pallet::<T>::block_number(); | ||
if swap.deadline > now { | ||
let item = Item::<T, I>::get(&offered_collection_id, &offered_item_id) | ||
.ok_or(Error::<T, I>::UnknownItem)?; | ||
ensure!(item.owner == caller, Error::<T, I>::NoPermission); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if it's past the deadline and I'm not the owner is it right that I can cancel the swap (or is that not possible)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, right. Anyone could cancel an outdated swap and clear the storage There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But there is no incentive for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Curious how this is usually tackled. I see two options
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the same time this could be done as a follow-up I guess, depending on the urgency. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ruseinov I saw deposit for the storage recored in Asset pallet. The amount stored within the same record. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, we either need to add deposits for everything data related, so this would incentivise users to clear the data. |
||
|
||
PendingSwapOf::<T, I>::remove(&offered_collection_id, &offered_item_id); | ||
|
||
Self::deposit_event(Event::SwapCancelled { | ||
offered_collection: offered_collection_id, | ||
offered_item: offered_item_id, | ||
desired_collection: swap.desired_collection, | ||
desired_item: swap.desired_item, | ||
price: swap.price, | ||
deadline: swap.deadline, | ||
}); | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn do_claim_swap( | ||
caller: T::AccountId, | ||
send_collection_id: T::CollectionId, | ||
send_item_id: T::ItemId, | ||
receive_collection_id: T::CollectionId, | ||
receive_item_id: T::ItemId, | ||
witness_price: Option<PriceWithDirection<ItemPrice<T, I>>>, | ||
) -> DispatchResult { | ||
let send_item = Item::<T, I>::get(&send_collection_id, &send_item_id) | ||
.ok_or(Error::<T, I>::UnknownItem)?; | ||
let receive_item = Item::<T, I>::get(&receive_collection_id, &receive_item_id) | ||
.ok_or(Error::<T, I>::UnknownItem)?; | ||
let swap = PendingSwapOf::<T, I>::get(&receive_collection_id, &receive_item_id) | ||
.ok_or(Error::<T, I>::UnknownSwap)?; | ||
|
||
ensure!(send_item.owner == caller, Error::<T, I>::NoPermission); | ||
ensure!( | ||
swap.desired_collection == send_collection_id && swap.price == witness_price, | ||
Error::<T, I>::UnknownSwap | ||
); | ||
|
||
if let Some(desired_item) = swap.desired_item { | ||
ensure!(desired_item == send_item_id, Error::<T, I>::UnknownSwap); | ||
} | ||
|
||
let now = frame_system::Pallet::<T>::block_number(); | ||
ensure!(now <= swap.deadline, Error::<T, I>::DeadlineExpired); | ||
|
||
if let Some(ref price) = swap.price { | ||
match price.direction { | ||
PriceDirection::Send => T::Currency::transfer( | ||
&receive_item.owner, | ||
&send_item.owner, | ||
price.amount, | ||
KeepAlive, | ||
)?, | ||
PriceDirection::Receive => T::Currency::transfer( | ||
&send_item.owner, | ||
&receive_item.owner, | ||
price.amount, | ||
KeepAlive, | ||
)?, | ||
}; | ||
} | ||
|
||
// This also removes the swap. | ||
Self::do_transfer(send_collection_id, send_item_id, receive_item.owner.clone(), |_, _| { | ||
jsidorenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok(()) | ||
})?; | ||
Self::do_transfer( | ||
receive_collection_id, | ||
receive_item_id, | ||
send_item.owner.clone(), | ||
|_, _| Ok(()), | ||
)?; | ||
gilescope marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Self::deposit_event(Event::SwapClaimed { | ||
sent_collection: send_collection_id, | ||
sent_item: send_item_id, | ||
sent_item_owner: send_item.owner, | ||
received_collection: receive_collection_id, | ||
received_item: receive_item_id, | ||
received_item_owner: receive_item.owner, | ||
price: swap.price, | ||
deadline: swap.deadline, | ||
}); | ||
|
||
Ok(()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use a
saturating_add
here and below in case someone usesu64::MAX
as duration.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done