Skip to content

Commit

Permalink
Merge pull request #234 from Szegoo/addition-removal
Browse files Browse the repository at this point in the history
Equippables addition/removal feature
  • Loading branch information
ilionic authored Nov 1, 2022
2 parents cd298ac + 74f3652 commit 1b64a15
Show file tree
Hide file tree
Showing 6 changed files with 445 additions and 8 deletions.
27 changes: 24 additions & 3 deletions pallets/rmrk-equip/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,10 @@ where
issuer: T::AccountId,
base_id: BaseId,
part_id: PartId,
equippables: EquippableList<BoundedVec<CollectionId, T::MaxCollectionsEquippablePerPart>>,
operation: EquippableOperation<
CollectionId,
BoundedVec<CollectionId, T::MaxCollectionsEquippablePerPart>,
>,
) -> Result<(BaseId, SlotId), DispatchError> {
// Caller must be issuer of base
match Bases::<T>::get(base_id) {
Expand All @@ -474,8 +477,26 @@ where
Err(Error::<T>::NoEquippableOnFixedPart.into())
},
PartType::SlotPart(mut slot_part) => {
// Update equippable value
slot_part.equippable = equippables;
match operation {
EquippableOperation::Add(equippable) => {
if let EquippableList::Custom(mut equippables) =
slot_part.equippable
{
let _ = equippables.try_push(equippable).map_err(|_| Error::<T>::TooManyEquippables)?;
slot_part.equippable = EquippableList::Custom(equippables);
}
},
EquippableOperation::Remove(equippable) =>
if let EquippableList::Custom(mut equippables) =
slot_part.equippable
{
equippables.retain(|e| *e != equippable);
slot_part.equippable = EquippableList::Custom(equippables);
},
EquippableOperation::Override(equippables) => {
slot_part.equippable = equippables;
},
};
// Overwrite Parts entry for this base_id.part_id
Parts::<T>::insert(base_id, part_id, PartType::SlotPart(slot_part));
Ok((base_id, part_id))
Expand Down
67 changes: 64 additions & 3 deletions pallets/rmrk-equip/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use sp_runtime::traits::StaticLookup;
pub use pallet::*;

use rmrk_traits::{
primitives::*, AccountIdOrCollectionNftTuple, Base, BaseInfo, EquippableList, PartType, Theme,
ThemeProperty,
base::EquippableOperation, primitives::*, AccountIdOrCollectionNftTuple, Base, BaseInfo,
EquippableList, PartType, Theme, ThemeProperty,
};

use sp_std::vec::Vec;
Expand Down Expand Up @@ -181,6 +181,8 @@ pub mod pallet {
EquipperDoesntExist,
// BaseID exceeds max value
NoAvailableBaseId,
// The `MaxCollectionsEquippablePerPart` limit was reached.
TooManyEquippables,
// PartId exceeds max value
NoAvailablePartId,
// Equipper is not direct parent of item, cannot equip
Expand Down Expand Up @@ -352,7 +354,66 @@ pub mod pallet {
) -> DispatchResult {
let sender = ensure_signed(origin)?;

let (base_id, slot_id) = Self::do_equippable(sender, base_id, slot_id, equippables)?;
let (base_id, slot_id) = Self::do_equippable(
sender,
base_id,
slot_id,
EquippableOperation::Override(equippables),
)?;

Self::deposit_event(Event::EquippablesUpdated { base_id, slot_id });
Ok(())
}

/// Adds a new collection that is allowed to be equipped to a Base's specified Slot Part.
///
/// Parameters:
/// - origin: The caller of the function, must be issuer of the base
/// - base_id: The Base containing the Slot Part to be updated
/// - part_id: The Slot Part whose Equippable List is being updated
/// - equippable: The equippable that will be added to the current Equippaables list
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())]
pub fn equippable_add(
origin: OriginFor<T>,
base_id: BaseId,
slot_id: SlotId,
equippable: CollectionId,
) -> DispatchResult {
let sender = ensure_signed(origin)?;

let (base_id, slot_id) = Self::do_equippable(
sender,
base_id,
slot_id,
EquippableOperation::Add(equippable),
)?;

Self::deposit_event(Event::EquippablesUpdated { base_id, slot_id });
Ok(())
}

/// Remove a collection from the equippables list.
///
/// Parameters:
/// - origin: The caller of the function, must be issuer of the base
/// - base_id: The Base containing the Slot Part to be updated
/// - part_id: The Slot Part whose Equippable List is being updated
/// - equippable: The equippable that will be removed from the current Equippaables list
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1).ref_time())]
pub fn equippable_remove(
origin: OriginFor<T>,
base_id: BaseId,
slot_id: SlotId,
equippable: CollectionId,
) -> DispatchResult {
let sender = ensure_signed(origin)?;

let (base_id, slot_id) = Self::do_equippable(
sender,
base_id,
slot_id,
EquippableOperation::Remove(equippable),
)?;

Self::deposit_event(Event::EquippablesUpdated { base_id, slot_id });
Ok(())
Expand Down
241 changes: 241 additions & 0 deletions pallets/rmrk-equip/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,247 @@ fn equippable_works() {
});
}

#[test]
fn equippable_add_works() {
ExtBuilder::default().build().execute_with(|| {
// First we'll build our parts
// Fixed part body 1 is one option for body type
let fixed_part_body_1 = FixedPart { id: 101, z: 0, src: stb("body-1") };
// Fixed part body 2 is second option for body type
let fixed_part_body_2 = FixedPart { id: 102, z: 0, src: stb("body-2") };
// Slot part left hand can equip items from collections 0 or 1
let slot_part_left_hand = SlotPart {
id: 201,
z: 0,
src: Some(stb("left-hand")),
equippable: EquippableList::Custom(bvec![
0, // Collection 0
1, // Collection 1
]),
};
// Slot part right hand can equip items from collections 2 or 3
let slot_part_right_hand = SlotPart {
id: 202,
z: 0,
src: Some(stb("right-hand")),
equippable: EquippableList::Custom(bvec![
2, // Collection 2
3, // Collection 3
]),
};
// Let's create a base with these 4 parts
assert_ok!(RmrkEquip::create_base(
Origin::signed(ALICE), // origin
stb("svg"), // base_type
stb("KANPEOPLE"), // symbol
bvec![
PartType::FixedPart(fixed_part_body_1),
PartType::FixedPart(fixed_part_body_2),
PartType::SlotPart(slot_part_left_hand),
PartType::SlotPart(slot_part_right_hand),
],
));

// add_equippable extrinsic should work
assert_ok!(RmrkEquip::equippable_add(
Origin::signed(ALICE),
0, // base ID
202, // slot ID
5, // equippable collection
));

// Last event should be EquippablesUpdated
System::assert_last_event(MockEvent::RmrkEquip(crate::Event::EquippablesUpdated {
base_id: 0,
slot_id: 202,
}));

// Parts storage should be updated
let should_be = SlotPart {
id: 202,
z: 0,
src: Some(stb("right-hand")),
equippable: EquippableList::Custom(bvec![2, 3, 5]),
};
assert_eq!(RmrkEquip::parts(0, 202).unwrap(), PartType::SlotPart(should_be));

// equippable limit is 10.
for _i in 2..9 {
assert_ok!(RmrkEquip::equippable_add(
Origin::signed(ALICE),
0, // base ID
202, // slot ID
5, // equippable collection
));
}

// This should fail since the limit is reached.
assert_noop!(
RmrkEquip::equippable_add(
Origin::signed(ALICE),
0, // base ID
202, // slot ID
5, // equippable collection
),
Error::<Test>::TooManyEquippables
);

// Should not be able to change equippable on non-existent base
assert_noop!(
RmrkEquip::equippable_add(
Origin::signed(ALICE),
666, // base ID
202, // slot ID
5, // equippable collection
),
Error::<Test>::BaseDoesntExist
);

// Should not be able to change equippable on non-existent part
assert_noop!(
RmrkEquip::equippable_add(
Origin::signed(ALICE),
0, // base ID
200, // slot ID
5, // equippable collection
),
Error::<Test>::PartDoesntExist
);

// Should not be able to change equippable on FixedPart part
assert_noop!(
RmrkEquip::equippable_add(
Origin::signed(ALICE),
0, // base ID
101, // slot ID
5, // equippable collection
),
Error::<Test>::NoEquippableOnFixedPart
);

// Should not be able to change equippable on non-issued base
assert_noop!(
RmrkEquip::equippable_add(
Origin::signed(BOB),
0, // base ID
201, // slot ID
3, // equippable collection
),
Error::<Test>::PermissionError
);
})
}

#[test]
fn equippable_remove_works() {
ExtBuilder::default().build().execute_with(|| {
// First we'll build our parts
// Fixed part body 1 is one option for body type
let fixed_part_body_1 = FixedPart { id: 101, z: 0, src: stb("body-1") };
// Fixed part body 2 is second option for body type
let fixed_part_body_2 = FixedPart { id: 102, z: 0, src: stb("body-2") };
// Slot part left hand can equip items from collections 0 or 1
let slot_part_left_hand = SlotPart {
id: 201,
z: 0,
src: Some(stb("left-hand")),
equippable: EquippableList::Custom(bvec![
0, // Collection 0
1, // Collection 1
]),
};
// Slot part right hand can equip items from collections 2 or 3
let slot_part_right_hand = SlotPart {
id: 202,
z: 0,
src: Some(stb("right-hand")),
equippable: EquippableList::Custom(bvec![
2, // Collection 2
3, // Collection 3
]),
};
// Let's create a base with these 4 parts
assert_ok!(RmrkEquip::create_base(
Origin::signed(ALICE), // origin
stb("svg"), // base_type
stb("KANPEOPLE"), // symbol
bvec![
PartType::FixedPart(fixed_part_body_1),
PartType::FixedPart(fixed_part_body_2),
PartType::SlotPart(slot_part_left_hand),
PartType::SlotPart(slot_part_right_hand),
],
));

// add_equippable extrinsic should work
assert_ok!(RmrkEquip::equippable_remove(
Origin::signed(ALICE),
0, // base ID
202, // slot ID
3, // equippable collection
));

// Last event should be EquippablesUpdated
System::assert_last_event(MockEvent::RmrkEquip(crate::Event::EquippablesUpdated {
base_id: 0,
slot_id: 202,
}));

// Parts storage should be updated
let should_be = SlotPart {
id: 202,
z: 0,
src: Some(stb("right-hand")),
equippable: EquippableList::Custom(bvec![2]),
};
assert_eq!(RmrkEquip::parts(0, 202).unwrap(), PartType::SlotPart(should_be));

// Should not be able to change equippable on non-existent base
assert_noop!(
RmrkEquip::equippable_remove(
Origin::signed(ALICE),
666, // base ID
202, // slot ID
2, // equippable collection
),
Error::<Test>::BaseDoesntExist
);

// Should not be able to change equippable on non-existent part
assert_noop!(
RmrkEquip::equippable_remove(
Origin::signed(ALICE),
0, // base ID
200, // slot ID
2, // equippable collection
),
Error::<Test>::PartDoesntExist
);

// Should not be able to change equippable on FixedPart part
assert_noop!(
RmrkEquip::equippable_remove(
Origin::signed(ALICE),
0, // base ID
101, // slot ID
2, // equippable collection
),
Error::<Test>::NoEquippableOnFixedPart
);

// Should not be able to change equippable on non-issued base
assert_noop!(
RmrkEquip::equippable_remove(
Origin::signed(BOB),
0, // base ID
201, // slot ID
2, // equippable collection
),
Error::<Test>::PermissionError
);
})
}

/// Base: Basic theme_add tests
#[test]
fn theme_add_works() {
Expand Down
4 changes: 3 additions & 1 deletion tests/src/setEquippableList.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getApiConnection } from './substrate/substrate-api';
import { expectTxFailure } from './util/helpers';
import { createCollection, createBase, setEquippableList } from "./util/tx";
import { createCollection, createBase, setEquippableList, addToEquippableList, removeFromEquippableList } from "./util/tx";

describe("integration test: set slot's Equippable List", () => {
let api: any;
Expand Down Expand Up @@ -43,6 +43,8 @@ describe("integration test: set slot's Equippable List", () => {
await setEquippableList(api, alice, baseId, slotId, "All");
await setEquippableList(api, alice, baseId, slotId, "Empty");
await setEquippableList(api, alice, baseId, slotId, { "Custom": collectionIds });
await removeFromEquippableList(api, alice, baseId, slotId, collectionIds[0]);
await addToEquippableList(api, alice, baseId, slotId, collectionIds[0]);
});

it("[negative] unable to set equippable list of a slot of non-existing base", async () => {
Expand Down
Loading

0 comments on commit 1b64a15

Please sign in to comment.