diff --git a/src/bin/main.rs b/src/bin/main.rs
index a9ed329..ee9aa8c 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -350,6 +350,23 @@ fn main() {
                     }
                 }
             ).await.unwrap();
+            let item14 = entity_gateway.create_item(
+                NewItemEntity {
+                    item: ItemDetail::Weapon(
+                        item::weapon::Weapon {
+                            weapon: item::weapon::WeaponType::Vulcan,
+                            grind: 5,
+                            special: Some(item::weapon::WeaponSpecial::Charge),
+                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
+                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
+                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
+                            tekked: true,
+                        }
+                    ),
+                    location: ItemLocation::Inventory {
+                        character_id: character.id,
+                    }
+                }).await.unwrap();
 
             let equipped = item::EquippedEntity {
                 weapon: Some(item2_w.id),
@@ -360,7 +377,7 @@ fn main() {
             };
             entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
 
-            let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]);
+            let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13, item14]);
             entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
             entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
         }
diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs
index 02bd750..5f90986 100644
--- a/src/entity/gateway/postgres/models.rs
+++ b/src/entity/gateway/postgres/models.rs
@@ -597,6 +597,7 @@ pub enum PgItemLocationDetail {
         mag: u32,
     },
     Shop,
+    SoldToShop,
 }
 
 impl From<ItemLocation> for PgItemLocationDetail {
@@ -609,6 +610,7 @@ impl From<ItemLocation> for PgItemLocationDetail {
             ItemLocation::Consumed => PgItemLocationDetail::Consumed,
             ItemLocation::FedToMag{mag} => PgItemLocationDetail::FedToMag{mag: mag.0},
             ItemLocation::Shop => PgItemLocationDetail::Shop,
+            ItemLocation::SoldToShop => PgItemLocationDetail::SoldToShop,
         }
     }
 }
@@ -623,6 +625,7 @@ impl From<PgItemLocationDetail> for ItemLocation {
             PgItemLocationDetail::Consumed => ItemLocation::Consumed,
             PgItemLocationDetail::FedToMag{mag} => ItemLocation::FedToMag{mag: ItemEntityId(mag)},
             PgItemLocationDetail::Shop => ItemLocation::Shop,
+            PgItemLocationDetail::SoldToShop => ItemLocation::SoldToShop,
         }
     }
 }
diff --git a/src/entity/item/armor.rs b/src/entity/item/armor.rs
index 6d2489d..451852b 100644
--- a/src/entity/item/armor.rs
+++ b/src/entity/item/armor.rs
@@ -297,7 +297,7 @@ pub enum ArmorModifier {
 }
 
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
 pub struct Armor {
     pub armor: ArmorType,
     pub dfp: u8,
@@ -329,4 +329,74 @@ impl Armor {
             Err(ItemParseError::InvalidArmorBytes) // TODO: error handling if wrong bytes are given
         }
     }
+
+    pub fn is_rare_item(self) -> bool {
+        matches!(
+            self.armor,
+            ArmorType::HunterField
+                | ArmorType::RangerField
+                | ArmorType::ForceField
+                | ArmorType::RevivalGarment
+                | ArmorType::SpiritGarment
+                | ArmorType::StinkFrame
+                | ArmorType::DPartsVer101
+                | ArmorType::DPartsVer210
+                | ArmorType::ParasiteWearDeRol
+                | ArmorType::ParasiteWearNelgal
+                | ArmorType::ParasiteWearVajulla
+                | ArmorType::SensePlate
+                | ArmorType::GravitonPlate
+                | ArmorType::AttributePlate
+                | ArmorType::FlowensFrame
+                | ArmorType::CustomFrameVerOo
+                | ArmorType::DbsArmor
+                | ArmorType::GuardWave
+                | ArmorType::DfField
+                | ArmorType::LuminousField
+                | ArmorType::ChuChuFever
+                | ArmorType::LoveHeart
+                | ArmorType::FlameGarment
+                | ArmorType::VirusArmorLafuteria
+                | ArmorType::BrightnessCircle
+                | ArmorType::AuraField
+                | ArmorType::ElectroFrame
+                | ArmorType::SacredCloth
+                | ArmorType::SmokingPlate
+                | ArmorType::StarCuirass
+                | ArmorType::BlackHoundCuirass
+                | ArmorType::MorningPrayer
+                | ArmorType::BlackOdoshiDomaru
+                | ArmorType::RedOdoshiDomaru
+                | ArmorType::BlackOdoshiRedNimaidou
+                | ArmorType::BlueOdoshiVioletNimaidou
+                | ArmorType::DirtyLifejacket
+                | ArmorType::KroesSweater
+                | ArmorType::WeddingDress
+                | ArmorType::SonicteamArmor
+                | ArmorType::RedCoat
+                | ArmorType::Thirteen
+                | ArmorType::MotherGarb
+                | ArmorType::MotherGarbPlus
+                | ArmorType::DressPlate
+                | ArmorType::Sweetheart
+                | ArmorType::IgnitionCloak
+                | ArmorType::CongealCloak
+                | ArmorType::TempestCloak
+                | ArmorType::CursedCloak
+                | ArmorType::SelectCloak
+                | ArmorType::SpiritCuirass
+                | ArmorType::RevivalCuriass
+                | ArmorType::AllianceUniform
+                | ArmorType::OfficerUniform
+                | ArmorType::CommanderUniform
+                | ArmorType::CrimsonCoat
+                | ArmorType::InfantryGear
+                | ArmorType::LieutenantGear
+                | ArmorType::InfantryMantle
+                | ArmorType::LieutenantMantle
+                | ArmorType::UnionField
+                | ArmorType::SamuraiArmor
+                | ArmorType::StealthSuit
+        )
+    }
 }
diff --git a/src/entity/item/esweapon.rs b/src/entity/item/esweapon.rs
index 908b026..3a88ccd 100644
--- a/src/entity/item/esweapon.rs
+++ b/src/entity/item/esweapon.rs
@@ -253,6 +253,11 @@ impl ESWeapon {
             name,
         }
     }
+
+    // TODO: this isn't even needed. all sranks are rare and only sell for 10? meseta in the shop
+    pub fn is_rare_item(self) -> bool {
+        true
+    }
 }
 
 #[cfg(test)]
diff --git a/src/entity/item/mag.rs b/src/entity/item/mag.rs
index 4f36983..087ee88 100644
--- a/src/entity/item/mag.rs
+++ b/src/entity/item/mag.rs
@@ -1098,6 +1098,53 @@ impl Mag {
             MagCell::LibertaKit => MagType::Agastya,
         }
     }
+
+    // TODO: is this even needed? mags are not shop sellable...yet
+    pub fn is_rare_item(self) -> bool {
+        matches!(
+            self.mag,
+            MagType::Pitri
+                | MagType::Soniti
+                | MagType::Preta
+                | MagType::Churel
+                | MagType::Robochao
+                | MagType::OpaOpa
+                | MagType::Pian
+                | MagType::Chao
+                | MagType::ChuChu
+                | MagType::KapuKapu
+                | MagType::AngelsWing
+                | MagType::DevilsWing
+                | MagType::Elenor
+                | MagType::MarkIII
+                | MagType::MasterSystem
+                | MagType::Genesis
+                | MagType::SegaSaturn
+                | MagType::Dreamcast
+                | MagType::Hamburger
+                | MagType::PanzersTail
+                | MagType::DevilsTail
+                | MagType::Deva
+                | MagType::Rati
+                | MagType::Savitri
+                | MagType::Rukmin
+                | MagType::Pushan
+                | MagType::Diwari
+                | MagType::Sato
+                | MagType::Bhima
+                | MagType::Nidra
+                | MagType::GeungSi
+                | MagType::Tellusis
+                | MagType::StrikerUnit
+                | MagType::Pioneer
+                | MagType::Puyo
+                | MagType::Moro
+                | MagType::Rappy
+                | MagType::Yahoo
+                | MagType::GaelGiel
+                | MagType::Agastya
+        )
+    }
 }
 
 
diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs
index 9959f54..8e8c6d4 100644
--- a/src/entity/item/mod.rs
+++ b/src/entity/item/mod.rs
@@ -47,6 +47,7 @@ pub enum ItemLocation {
         mag: ItemEntityId,
     },
     Shop,
+    SoldToShop,
     /*Destroyed {
         // marks an item that has been consumed in some way
     },
diff --git a/src/entity/item/shield.rs b/src/entity/item/shield.rs
index 5784fd4..5c42598 100644
--- a/src/entity/item/shield.rs
+++ b/src/entity/item/shield.rs
@@ -548,4 +548,154 @@ impl Shield {
             Err(ItemParseError::InvalidShieldBytes) // TODO: error handling if wrong bytes are given
         }
     }
+
+    pub fn is_rare_item(self) -> bool {
+        matches!(
+            self.shield,
+            ShieldType::InvisibleGuard
+                | ShieldType::SacredGuard
+                | ShieldType::SPartsVer116
+                | ShieldType::SPartsVer201
+                | ShieldType::LightRelief
+                | ShieldType::ShieldOfDelsaber
+                | ShieldType::ForceWall
+                | ShieldType::RangerWall
+                | ShieldType::HunterWall
+                | ShieldType::AttributeWall
+                | ShieldType::SecretGear
+                | ShieldType::CombatGear
+                | ShieldType::ProtoRegeneGear
+                | ShieldType::RegenerateGear
+                | ShieldType::RegeneGearAdv
+                | ShieldType::FlowensShield
+                | ShieldType::CustomBarrierVerOo
+                | ShieldType::DbsShield
+                | ShieldType::RedRing
+                | ShieldType::TripolicShield
+                | ShieldType::StandstillShield
+                | ShieldType::SafetyHeart
+                | ShieldType::KasamiBracer
+                | ShieldType::GodsShieldSuzaku
+                | ShieldType::GodsShieldGenbu
+                | ShieldType::GodsShieldByakko
+                | ShieldType::GodsShieldSeiryu
+                | ShieldType::HuntersShell
+                | ShieldType::RicosGlasses
+                | ShieldType::RicosEarring
+                | ShieldType::BlueRing
+                | ShieldType::Barrier2
+                | ShieldType::SecureFeet
+                | ShieldType::Barrier3
+                | ShieldType::Barrier4
+                | ShieldType::Barrier5
+                | ShieldType::Barrier6
+                | ShieldType::RestaMerge
+                | ShieldType::AntiMerge
+                | ShieldType::ShiftaMerge
+                | ShieldType::DebandMerge
+                | ShieldType::FoieMerge
+                | ShieldType::GifoieMerge
+                | ShieldType::RafoieMerge
+                | ShieldType::RedMerge
+                | ShieldType::BartaMerge
+                | ShieldType::GibartaMerge
+                | ShieldType::RabartaMerge
+                | ShieldType::BlueMerge
+                | ShieldType::ZondeMerge
+                | ShieldType::GizondeMerge
+                | ShieldType::RazondeMerge
+                | ShieldType::YellowMerge
+                | ShieldType::RecoveryBarrier
+                | ShieldType::AssistBarrier
+                | ShieldType::RedBarrier
+                | ShieldType::BlueBarrier
+                | ShieldType::YellowBarrier
+                | ShieldType::WeaponsGoldShield
+                | ShieldType::BlackGear
+                | ShieldType::WorksGuard
+                | ShieldType::RagolRing
+                | ShieldType::BlueRing2
+                | ShieldType::BlueRing3
+                | ShieldType::BlueRing4
+                | ShieldType::BlueRing5
+                | ShieldType::BlueRing6
+                | ShieldType::BlueRing7
+                | ShieldType::BlueRing8
+                | ShieldType::BlueRing9
+                | ShieldType::GreenRing
+                | ShieldType::GreenRing2
+                | ShieldType::GreenRing3
+                | ShieldType::GreenRing4
+                | ShieldType::GreenRing5
+                | ShieldType::GreenRing6
+                | ShieldType::GreenRing7
+                | ShieldType::GreenRing8
+                | ShieldType::YellowRing
+                | ShieldType::YellowRing2
+                | ShieldType::YellowRing3
+                | ShieldType::YellowRing4
+                | ShieldType::YellowRing5
+                | ShieldType::YellowRing6
+                | ShieldType::YellowRing7
+                | ShieldType::YellowRing8
+                | ShieldType::PurpleRing
+                | ShieldType::PurpleRing2
+                | ShieldType::PurpleRing3
+                | ShieldType::PurpleRing4
+                | ShieldType::PurpleRing5
+                | ShieldType::PurpleRing6
+                | ShieldType::PurpleRing7
+                | ShieldType::PurpleRing8
+                | ShieldType::WhiteRing
+                | ShieldType::WhiteRing2
+                | ShieldType::WhiteRing3
+                | ShieldType::WhiteRing4
+                | ShieldType::WhiteRing5
+                | ShieldType::WhiteRing6
+                | ShieldType::WhiteRing7
+                | ShieldType::WhiteRing8
+                | ShieldType::BlackRing
+                | ShieldType::BlackRing2
+                | ShieldType::BlackRing3
+                | ShieldType::BlackRing4
+                | ShieldType::BlackRing5
+                | ShieldType::BlackRing6
+                | ShieldType::BlackRing7
+                | ShieldType::BlackRing8
+                | ShieldType::WeaponsSilverShield
+                | ShieldType::WeaponsCopperShield
+                | ShieldType::Gratia
+                | ShieldType::TripolicReflector
+                | ShieldType::StrikerPlus
+                | ShieldType::RegenerateGearBP
+                | ShieldType::Rupika
+                | ShieldType::YataMirror
+                | ShieldType::BunnyEars
+                | ShieldType::CatEars
+                | ShieldType::ThreeSeals
+                | ShieldType::GodsShieldKouryu
+                | ShieldType::DfShield
+                | ShieldType::FromTheDepths
+                | ShieldType::DeRolLeShield
+                | ShieldType::HoneycombReflector
+                | ShieldType::Epsiguard
+                | ShieldType::AngelRing
+                | ShieldType::UnionGuard
+                | ShieldType::UnionGuard2
+                | ShieldType::UnionGuard3
+                | ShieldType::UnionGuard4
+                | ShieldType::StinkShield
+                | ShieldType::Unknownb
+                | ShieldType::Genpei
+                | ShieldType::Genpei2
+                | ShieldType::Genpei3
+                | ShieldType::Genpei4
+                | ShieldType::Genpei5
+                | ShieldType::Genpei6
+                | ShieldType::Genpei7
+                | ShieldType::Genpei8
+                | ShieldType::Genpei9
+                | ShieldType::Genpei10
+        )
+    }
 }
diff --git a/src/entity/item/tool.rs b/src/entity/item/tool.rs
index 054a922..f24f596 100644
--- a/src/entity/item/tool.rs
+++ b/src/entity/item/tool.rs
@@ -680,4 +680,174 @@ impl Tool {
     pub fn max_stack(&self) -> usize {
         self.tool.max_stack()
     }
+
+    pub fn is_rare_item(self) -> bool {
+        matches!(
+            self.tool,
+            ToolType::CellOfMag502
+                | ToolType::CellOfMag213
+                | ToolType::PartsOfRobochao
+                | ToolType::HeartOfOpaOpa
+                | ToolType::HeartOfPian
+                | ToolType::HeartOfChao
+                | ToolType::SorcerersRightArm
+                | ToolType::SBeatsArms
+                | ToolType::PArmsArms
+                | ToolType::DelsabersRightArm
+                | ToolType::BringersRightArm
+                | ToolType::DelsabersLeftArm
+                | ToolType::SRedsArms
+                | ToolType::DragonsClaw
+                | ToolType::HildebearsHead
+                | ToolType::HildebluesHead
+                | ToolType::PartsOfBaranz
+                | ToolType::BelrasRightArm
+                | ToolType::GiGuesBody
+                | ToolType::SinowBerillsArms
+                | ToolType::GrassAssassinsArms
+                | ToolType::BoomasRightArm
+                | ToolType::GoboomasRightArm
+                | ToolType::GigoboomasRightArm
+                | ToolType::GalGryphonsWing
+                | ToolType::RappysWing
+                | ToolType::CladdingOfEpsilon
+                | ToolType::DeRolLeShell
+                | ToolType::BerillPhoton
+                | ToolType::ParasiticGeneFlow
+                | ToolType::MagicStoneIritista
+                | ToolType::BlueBlackStone
+                | ToolType::Syncesta
+                | ToolType::MagicWater
+                | ToolType::ParasiticCellTypeD
+                | ToolType::MagicRockHeartKey
+                | ToolType::MagicRockMoola
+                | ToolType::StarAmplifier
+                | ToolType::BookOfHitogata
+                | ToolType::HeartOfChuChu
+                | ToolType::PartsOfEggBlaster
+                | ToolType::HeartOfAngel
+                | ToolType::HeartOfDevil
+                | ToolType::KitOfHamburger
+                | ToolType::PanthersSpirit
+                | ToolType::KitOfMark3
+                | ToolType::KitOfMasterSystem
+                | ToolType::KitOfGenesis
+                | ToolType::KitOfSegaSaturn
+                | ToolType::KitOfDreamcast
+                | ToolType::AmplifierOfResta
+                | ToolType::AmplifierOfAnti
+                | ToolType::AmplifierOfShifta
+                | ToolType::AmplifierOfDeband
+                | ToolType::AmplifierOfFoie
+                | ToolType::AmplifierOfGifoie
+                | ToolType::AmplifierOfRafoie
+                | ToolType::AmplifierOfBarta
+                | ToolType::AmplifierOfGibarta
+                | ToolType::AmplifierOfRabarta
+                | ToolType::AmplifierOfZonde
+                | ToolType::AmplifierOfGizonde
+                | ToolType::AmplifierOfRazonde
+                | ToolType::AmplifierOfRed
+                | ToolType::AmplifierOfBlue
+                | ToolType::AmplifierOfYellow
+                | ToolType::HeartOfKapuKapu
+                | ToolType::PhotonBooster
+                | ToolType::Addslot
+                | ToolType::PhotonDrop
+                | ToolType::PhotonSphere
+                | ToolType::PhotonCrystal
+                | ToolType::SecretTicket
+                | ToolType::PhotonTicket
+                | ToolType::BookOfKatana1
+                | ToolType::BookOfKatana2
+                | ToolType::BookOfKatana3
+                | ToolType::WeaponsBronzeBadge
+                | ToolType::WeaponsSilverBadge
+                | ToolType::WeaponsGoldBadge
+                | ToolType::WeaponsCrystalBadge
+                | ToolType::WeaponsSteelBadge
+                | ToolType::WeaponsAluminumBadge
+                | ToolType::WeaponsLeatherBadge
+                | ToolType::WeaponsBoneBadge
+                | ToolType::LetterOfAppreciation
+                | ToolType::ItemTicket
+                | ToolType::ValentinesChocolate
+                | ToolType::NewYearsCard
+                | ToolType::ChristmasCard
+                | ToolType::BirthdayCard
+                | ToolType::ProofOfSonicTeam
+                | ToolType::SpecialEventTicket
+                | ToolType::FlowerBouquet
+                | ToolType::Cake
+                | ToolType::Accessories
+                | ToolType::MrNakasBusinessCard
+                | ToolType::Present
+                | ToolType::Chocolate
+                | ToolType::Candy
+                | ToolType::Cake2
+                | ToolType::WeaponsSilverBadge2
+                | ToolType::WeaponsGoldBadge2
+                | ToolType::WeaponsCrystalBadge2
+                | ToolType::WeaponsSteelBadge2
+                | ToolType::WeaponsAluminumBadge2
+                | ToolType::WeaponsLeatherBadge2
+                | ToolType::WeaponsBoneBadge2
+                | ToolType::Bouquet
+                | ToolType::Decoction
+                | ToolType::ChristmasPresent
+                | ToolType::EasterEgg
+                | ToolType::JackOLantern
+                | ToolType::DiskVol1WeddingMarch
+                | ToolType::DiskVol2DayLight
+                | ToolType::DiskVol3BurningRangers
+                | ToolType::DiskVol4OpenYourHeart
+                | ToolType::DiskVol5LiveLearn
+                | ToolType::DiskVol6Nights
+                | ToolType::DiskVol7EndingThemePianoVer
+                | ToolType::DiskVol8HeartToHeart
+                | ToolType::DiskVol9StrangeBlue
+                | ToolType::DiskVol10ReunionSystem
+                | ToolType::DiskVol11Pinnacles
+                | ToolType::DiskVol12FightInsideTheSpaceship
+                | ToolType::HuntersReport
+                | ToolType::HuntersReport2
+                | ToolType::HuntersReport3
+                | ToolType::HuntersReport4
+                | ToolType::HuntersReport5
+                | ToolType::Tablet
+                | ToolType::Unknown2
+                | ToolType::DragonScale
+                | ToolType::HeavenStrikerCoat
+                | ToolType::PioneerParts
+                | ToolType::AmitiesMemo
+                | ToolType::HeartOfMorolian
+                | ToolType::RappysBeak
+                | ToolType::YahoosEngine
+                | ToolType::DPhotonCore
+                | ToolType::LibertaKit
+                | ToolType::CellOfMag0503
+                | ToolType::CellOfMag0504
+                | ToolType::CellOfMag0505
+                | ToolType::CellOfMag0506
+                | ToolType::CellOfMag0507
+                | ToolType::TeamPoints500
+                | ToolType::TeamPoints1000
+                | ToolType::TeamPoints5000
+                | ToolType::TeamPoints10000
+        )
+    }
+
+    // TODO: do we actually need this function?
+    pub fn is_material(self) -> bool {
+        matches!(
+            self.tool,
+            ToolType::PowerMaterial
+                | ToolType::MindMaterial
+                | ToolType::EvadeMaterial
+                | ToolType::HpMaterial
+                | ToolType::TpMaterial
+                | ToolType::DefMaterial
+                | ToolType::LuckMaterial
+        )
+    }
 }
diff --git a/src/entity/item/unit.rs b/src/entity/item/unit.rs
index df0b611..cc973ed 100644
--- a/src/entity/item/unit.rs
+++ b/src/entity/item/unit.rs
@@ -384,4 +384,66 @@ impl Unit {
             Err(ItemParseError::InvalidUnitBytes) // TODO: error handling if wrong bytes are given
         }
     }
+
+    pub fn is_rare_item(self) -> bool {
+        matches!(
+            self.unit,
+            UnitType::GodPower
+                | UnitType::GodMind
+                | UnitType::GodArm
+                | UnitType::GodLegs
+                | UnitType::GodHp
+                | UnitType::GodTp
+                | UnitType::GodBody
+                | UnitType::GodLuck
+                | UnitType::HeroAbility
+                | UnitType::GodAbility
+                | UnitType::AllResist
+                | UnitType::SuperResist
+                | UnitType::PerfectResist
+                | UnitType::HpRevival
+                | UnitType::TpRevival
+                | UnitType::PbAmplifier
+                | UnitType::PbGenerate
+                | UnitType::PbCreate
+                | UnitType::DevilTechnique
+                | UnitType::GodTechnique
+                | UnitType::DevilBattle
+                | UnitType::GodBattle
+                | UnitType::CurePoison
+                | UnitType::CureParalysis
+                | UnitType::CureSlow
+                | UnitType::CureConfuse
+                | UnitType::CureFreeze
+                | UnitType::CureShock
+                | UnitType::YasakaniMagatama
+                | UnitType::V101
+                | UnitType::V501
+                | UnitType::V502
+                | UnitType::V801
+                | UnitType::Limiter
+                | UnitType::Adept
+                | UnitType::SwordsmanLore
+                | UnitType::ProofOfSwordSaint
+                | UnitType::Smartlink
+                | UnitType::DivineProtection
+                | UnitType::HeavenlyBattle
+                | UnitType::HeavenlyPower
+                | UnitType::HeavenlyMind
+                | UnitType::HeavenlyArms
+                | UnitType::HeavenlyLegs
+                | UnitType::HeavenlyBody
+                | UnitType::HeavenlyLuck
+                | UnitType::HeavenlyAbility
+                | UnitType::CenturionAbility
+                | UnitType::FriendRing
+                | UnitType::HeavenlyHp
+                | UnitType::HeavenlyTp
+                | UnitType::HeavenlyResist
+                | UnitType::HeavenlyTechnique
+                | UnitType::HpRessurection
+                | UnitType::TpRessurection
+                | UnitType::PbIncrease
+        )
+    }
 }
diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs
index 6473dd8..9f4ea27 100644
--- a/src/entity/item/weapon.rs
+++ b/src/entity/item/weapon.rs
@@ -1454,7 +1454,7 @@ pub enum WeaponModifier {
     },
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
 pub struct Weapon {
     pub weapon: WeaponType,
     pub special: Option<WeaponSpecial>,
@@ -1584,6 +1584,68 @@ impl Weapon {
             Err(ItemParseError::InvalidWeaponBytes) // TODO: error handling if wrong bytes are given
         }
     }
+
+    // TODO: invert this? ie: handgun, saber, dagger etc. => false, _ => true?
+    pub fn is_rare_item(self) -> bool {
+        !matches!(
+            self.weapon,
+            WeaponType::Saber
+                | WeaponType::Brand
+                | WeaponType::Buster
+                | WeaponType::Pallasch
+                | WeaponType::Gladius
+                | WeaponType::Sword
+                | WeaponType::Gigush
+                | WeaponType::Breaker
+                | WeaponType::Claymore
+                | WeaponType::Calibur
+                | WeaponType::Dagger
+                | WeaponType::Knife
+                | WeaponType::Blade
+                | WeaponType::Edge
+                | WeaponType::Ripper
+                | WeaponType::Partisan
+                | WeaponType::Halbert
+                | WeaponType::Glaive
+                | WeaponType::Berdys
+                | WeaponType::Gungnir
+                | WeaponType::Slicer
+                | WeaponType::Spinner
+                | WeaponType::Cutter
+                | WeaponType::Sawcer
+                | WeaponType::Diska
+                | WeaponType::Handgun
+                | WeaponType::Autogun
+                | WeaponType::Lockgun
+                | WeaponType::Railgun
+                | WeaponType::Raygun
+                | WeaponType::Rifle
+                | WeaponType::Sniper
+                | WeaponType::Blaster
+                | WeaponType::Beam
+                | WeaponType::Laser
+                | WeaponType::Mechgun
+                | WeaponType::Assault
+                | WeaponType::Repeater
+                | WeaponType::Gatling
+                | WeaponType::Vulcan
+                | WeaponType::Shot
+                | WeaponType::Spread
+                | WeaponType::Cannon
+                | WeaponType::Launcher
+                | WeaponType::Arms
+                | WeaponType::Cane
+                | WeaponType::Stick
+                | WeaponType::Mace
+                | WeaponType::Club
+                | WeaponType::Rod
+                | WeaponType::Pole
+                | WeaponType::Pillar
+                | WeaponType::Striker
+                | WeaponType::Wand
+                | WeaponType::Staff
+                | WeaponType::Baton
+                | WeaponType::Scepter
+        )
+    }
 }
-
-
diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs
index 9589849..7369c40 100644
--- a/src/ship/items/inventory.rs
+++ b/src/ship/items/inventory.rs
@@ -1,13 +1,14 @@
 use std::cmp::Ordering;
 use thiserror::Error;
-use libpso::character::character;//::InventoryItem;
+use libpso::character::character;
 use crate::entity::character::CharacterEntityId;
 use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
-use crate::entity::item::tool::Tool;
+use crate::entity::item::tool::{Tool, ToolType};
 use crate::entity::item::mag::Mag;
 use crate::entity::item::weapon::Weapon;
-use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
+use crate::ship::items::{ClientItemId, BankItem, BankItemHandle, ItemManagerError};
 use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
+use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem};
 
 const INVENTORY_CAPACITY: usize = 30;
 
@@ -220,6 +221,62 @@ impl InventoryItem {
             _ => None
         }
     }
+
+    pub fn get_sell_price(&self) -> Result<u32, ItemManagerError> {
+        match self {
+            InventoryItem::Individual(individual_item) => {
+                match &individual_item.item {
+                    // TODO: can wrapped items be sold?
+                    ItemDetail::Weapon(w) => {
+                        if !w.tekked {
+                            return Ok(1u32)
+                        }
+                        if w.is_rare_item() {
+                            return Ok(10u32)
+                        }
+                        Ok((WeaponShopItem::from(w).price() / 8) as u32)
+                    },
+                    ItemDetail::Armor(a) => {
+                        if a.is_rare_item() {
+                            return Ok(10u32)
+                        }
+                        Ok((ArmorShopItem::from(a).price() / 8) as u32)
+                    },
+                    ItemDetail::Shield(s) => {
+                        if s.is_rare_item() {
+                            return Ok(10u32)
+                        }
+                        Ok((ArmorShopItem::from(s).price() / 8) as u32)
+                    },
+                    ItemDetail::Unit(u) => {
+                        if u.is_rare_item() {
+                            return Ok(10u32)
+                        }
+                        Ok((ArmorShopItem::from(u).price() / 8) as u32)
+                    },
+                    ItemDetail::Tool(t) => {
+                        if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() {
+                            return Ok(10u32)
+                        }
+                        Ok((ToolShopItem::from(t).price() / 8) as u32)
+                    },
+                    ItemDetail::TechniqueDisk(d) => {
+                        Ok((ToolShopItem::from(d).price() / 8) as u32)
+                    },
+                    ItemDetail::Mag(_m) => {
+                        Err(ItemManagerError::ItemNotSellable(self.clone()))
+                    },
+                    ItemDetail::ESWeapon(_e) => {
+                        Ok(10u32)
+                    },
+                }
+            },
+            // the number of stacked items sold is handled by the caller. this is just the price of 1
+            InventoryItem::Stacked(stacked_item) => {
+                Ok((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32)
+            },
+        }
+    }
 }
 
 
diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs
index 441c85b..f22c3a5 100644
--- a/src/ship/items/manager.rs
+++ b/src/ship/items/manager.rs
@@ -1,5 +1,6 @@
 use crate::ship::items::ClientItemId;
 use std::collections::HashMap;
+use std::cmp::Ordering;
 use thiserror::Error;
 use crate::entity::gateway::EntityGateway;
 use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
@@ -55,6 +56,9 @@ pub enum ItemManagerError {
     NoArmorEquipped,
     GatewayError(#[from] crate::entity::gateway::GatewayError),
     StackedItemError(Vec<ItemEntity>),
+    ItemNotSellable(InventoryItem),
+    WalletFull,
+    InvalidSale,
 }
 
 pub struct ItemManager {
@@ -844,6 +848,45 @@ impl ItemManager {
         Ok(inventory_item)
     }
 
+    pub async fn player_sells_item<EG: EntityGateway>(&mut self,
+                                                    entity_gateway: &mut EG,
+                                                    character: &mut CharacterEntity,
+                                                    item_id: ClientItemId,
+                                                    amount: usize) 
+                                                    -> Result<(), anyhow::Error> {
+        let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
+        let sold_item_handle = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
+        if let Some(item_sold) = sold_item_handle.item() {
+            let unit_price = item_sold.get_sell_price()?; {
+                let total_sale = unit_price * amount as u32;
+                if character.meseta + total_sale <= 999999 {
+                    character.meseta += total_sale;
+                    match item_sold {
+                        InventoryItem::Individual(i) => {
+                            entity_gateway.change_item_location(&i.entity_id, ItemLocation::SoldToShop).await?;
+                            inventory.remove_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
+                        },
+                        InventoryItem::Stacked(s) => {
+                            match amount.cmp(&s.count()) {
+                                Ordering::Less | Ordering::Equal => {
+                                    sold_item_handle.consume(amount)?;
+                                },
+                                Ordering::Greater => return Err(ItemManagerError::InvalidSale.into()),
+                            };
+                        },
+                    }
+                    entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
+                    entity_gateway.save_character(character).await?;
+                } else {
+                    return Err(ItemManagerError::WalletFull.into());
+                }
+            }
+        } else {
+            return Err(ItemManagerError::ItemIdNotInInventory(item_id).into())
+        }
+        Ok(())
+    }
+
     // TODO: check if slot exists before putting units into it
     pub async fn player_equips_item<EG: EntityGateway>(&mut self,
                                                        entity_gateway: &mut EG,
@@ -901,10 +944,9 @@ impl ItemManager {
             .ok_or(ItemManagerError::WrongItemType(item_id))?;
 
         let entity_id = individual.entity_id;
-        let mut weapon = individual
+        let mut weapon = *individual
             .weapon()
-            .ok_or(ItemManagerError::WrongItemType(item_id))?
-            .clone();
+            .ok_or(ItemManagerError::WrongItemType(item_id))?;
 
         weapon.apply_modifier(&tek);
         entity_gateway.add_weapon_modifier(&entity_id, tek).await?;
@@ -912,7 +954,7 @@ impl ItemManager {
         inventory.add_item(InventoryItem::Individual(IndividualInventoryItem {
             entity_id,
             item_id,
-            item: ItemDetail::Weapon(weapon.clone()),
+            item: ItemDetail::Weapon(weapon),
         }))?;
 
         entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs
index b2cb864..f17f1a1 100644
--- a/src/ship/packet/handler/direct_message.rs
+++ b/src/ship/packet/handler/direct_message.rs
@@ -370,11 +370,12 @@ where
         }
     };
 
-    if client.character.meseta < item.price() as u32 {
+    // TODO: stop giving away wares for free
+    if client.character.meseta < (item.price() * buy_item.amount as usize) as u32 {
         return Err(ShipError::ShopError.into())
     }
 
-    client.character.meseta -= item.price() as u32;
+    client.character.meseta -= (item.price() * buy_item.amount as usize)  as u32;
     entity_gateway.save_character(&client.character).await?;
 
     let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?;
@@ -435,11 +436,10 @@ where
     let inventory = item_manager.get_character_inventory(&client.character)?;
     let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id))
         .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
-    let mut weapon = item.individual()
+    let mut weapon = *item.individual()
         .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
         .weapon()
-        .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
-        .clone();
+        .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
 
     weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
         special: special_mod,
diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs
index 39d3717..1ded48e 100644
--- a/src/ship/packet/handler/message.rs
+++ b/src/ship/packet/handler/message.rs
@@ -110,7 +110,7 @@ pub fn drop_coordinates(id: ClientId,
         item_id: ClientItemId(drop_coordinates.item_id),
     });
 
-    Ok(Box::new(None.into_iter()))
+    Ok(Box::new(None.into_iter())) // TODO: do we need to send a packet here?
 }
 
 pub async fn no_longer_has_item<EG>(id: ClientId,
@@ -254,7 +254,7 @@ where
     if client.character.meseta >= charge.meseta {
         client.character.meseta -= charge.meseta;
         entity_gateway.save_character(&client.character).await?;
-        Ok(Box::new(None.into_iter()))
+        Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used charge and lost money?
     } else {
         Err(ShipError::NotEnoughMeseta(id, client.character.meseta).into())
     }
@@ -274,7 +274,7 @@ where
     let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?;
 
     item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?;
-    Ok(Box::new(None.into_iter()))
+    Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item
 }
 
 pub async fn player_used_medical_center<EG>(id: ClientId,
@@ -333,7 +333,7 @@ where
         0
     };
     item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), equip_slot).await?;
-    Ok(Box::new(None.into_iter()))
+    Ok(Box::new(None.into_iter())) // TODO: tell other players you equipped an item
 }
 
 pub async fn player_unequips_item<EG>(id: ClientId,
@@ -347,7 +347,7 @@ where
 {
     let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
     item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?;
-    Ok(Box::new(None.into_iter()))
+    Ok(Box::new(None.into_iter())) // TODO: tell other players if you unequip an item
 }
 
 pub async fn player_sorts_items<EG>(id: ClientId,
@@ -361,5 +361,21 @@ where
 {
     let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
     item_manager.player_sorts_items(entity_gateway, &client.character, pkt.item_ids).await?;
-    Ok(Box::new(None.into_iter())) // Do clients care about the order of other clients items?
+    Ok(Box::new(None.into_iter())) // TODO: clients probably care about each others item orders
+}
+
+pub async fn player_sells_item<EG> (id: ClientId,
+                            sold_item: &PlayerSoldItem,
+                            entity_gateway: &mut EG,
+                            // client_location: &ClientLocation,
+                            clients: &mut Clients,
+                            item_manager: &mut ItemManager)
+                            -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
+where
+    EG: EntityGateway
+{
+    let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
+    item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?;
+    // TODO: send the packet to other clients
+    Ok(Box::new(None.into_iter()))
 }
diff --git a/src/ship/ship.rs b/src/ship/ship.rs
index 8091647..ae9255d 100644
--- a/src/ship/ship.rs
+++ b/src/ship/ship.rs
@@ -485,6 +485,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
             GameMessage::SortItems(sort_items) => {
                 handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await?
             },
+            GameMessage::PlayerSoldItem(player_sold_item) => {
+                handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
+            },
             _ => {
                 let cmsg = msg.clone();
                 let block = self.blocks.with_client(id, &self.clients)?;
diff --git a/src/ship/shops/armor.rs b/src/ship/shops/armor.rs
index 789db56..83afca2 100644
--- a/src/ship/shops/armor.rs
+++ b/src/ship/shops/armor.rs
@@ -14,7 +14,7 @@ use crate::ship::item_stats::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS};
 
 #[derive(Debug)]
 pub enum ArmorShopItem {
-    Frame(ArmorType, usize),
+    Frame(ArmorType, usize), // slots
     Barrier(ShieldType),
     Unit(UnitType),
 }
@@ -90,6 +90,38 @@ impl ShopItem for ArmorShopItem {
     }
 }
 
+// impl ArmorShopItem {
+//     pub fn armor_from_item(a: &Armor) -> ArmorShopItem {
+//         ArmorShopItem::Frame(a.armor, a.slots as usize)
+//     }
+
+//     pub fn shield_from_item(s: &Shield) -> ArmorShopItem {
+//         ArmorShopItem::Barrier(s.shield)
+//     }
+
+//     pub fn unit_from_item(u: &Unit) -> ArmorShopItem {
+//         ArmorShopItem::Unit(u.unit)
+//     }
+// }
+
+impl From<&Armor> for ArmorShopItem {
+    fn from(armor: &Armor) -> ArmorShopItem {
+        ArmorShopItem::Frame(armor.armor, armor.slots as usize)
+    }
+}
+
+impl From<&Shield> for ArmorShopItem {
+    fn from(shield: &Shield) -> ArmorShopItem {
+        ArmorShopItem::Barrier(shield.shield)
+    }
+}
+
+impl From<&Unit> for ArmorShopItem {
+    fn from(unit: &Unit) -> ArmorShopItem {
+        ArmorShopItem::Unit(unit.unit)
+    }
+}
+
 
 #[derive(Debug, Deserialize, Clone)]
 struct FrameTierItem {
diff --git a/src/ship/shops/tool.rs b/src/ship/shops/tool.rs
index d3a8e4b..2a025c6 100644
--- a/src/ship/shops/tool.rs
+++ b/src/ship/shops/tool.rs
@@ -96,6 +96,28 @@ impl ShopItem for ToolShopItem {
     }
 }
 
+// impl ToolShopItem {
+//     pub fn tool_from_item(t: &Tool) -> ToolShopItem {
+//         ToolShopItem::Tool(t.tool)
+//     }
+
+//     pub fn tech_from_item(d: &TechniqueDisk) -> ToolShopItem {
+//         ToolShopItem::Tech(*d)
+//     }
+// }
+
+impl From<&Tool> for ToolShopItem {
+    fn from(tool: &Tool) -> ToolShopItem {
+        ToolShopItem::Tool(tool.tool)
+    }
+}
+
+impl From<&TechniqueDisk> for ToolShopItem {
+    fn from(techdisk: &TechniqueDisk) -> ToolShopItem {
+        ToolShopItem::Tech(*techdisk)
+    }
+}
+
 
 #[derive(Debug, Deserialize)]
 struct ToolTable(Vec<ToolType>);
diff --git a/src/ship/shops/weapon.rs b/src/ship/shops/weapon.rs
index 6818f23..1f53cf7 100644
--- a/src/ship/shops/weapon.rs
+++ b/src/ship/shops/weapon.rs
@@ -27,7 +27,7 @@ pub struct WeaponShopItem {
     weapon: WeaponType,
     special: Option<WeaponSpecial>,
     grind: usize,
-    attributes: [Option<WeaponAttribute>; 2],
+    attributes: [Option<WeaponAttribute>; 3],
 }
 
 impl PartialEq for WeaponShopItem {
@@ -53,6 +53,17 @@ impl PartialOrd for WeaponShopItem {
     }
 }
 
+impl From<&Weapon> for WeaponShopItem {
+    fn from(weapon: &Weapon) -> WeaponShopItem {
+        WeaponShopItem {
+            weapon: weapon.weapon,
+            special: weapon.special,
+            grind: weapon.grind as usize,
+            attributes: weapon.attrs,
+        }
+    }
+}
+
 
 fn special_stars(special: &WeaponSpecial) -> usize {
     match special {
@@ -120,7 +131,6 @@ impl ShopItem for WeaponShopItem {
                 }).unwrap_or(0.0);
 
                 price += special * special * 1000.0;
-
                 price as usize
             })
             .unwrap_or(0xFFFF)
@@ -143,6 +153,17 @@ impl ShopItem for WeaponShopItem {
     }
 }
 
+impl WeaponShopItem {
+    pub fn weapon_from_item(w: &Weapon) -> WeaponShopItem {
+        WeaponShopItem {
+            weapon: w.weapon,
+            special: w.special,
+            grind: w.grind as usize,
+            attributes: w.attrs,
+        }
+    }
+}
+
 
 #[derive(Debug, Deserialize)]
 struct WeaponTableTierEntry {
@@ -515,7 +536,7 @@ impl<R: Rng + SeedableRng> WeaponShop<R> {
             weapon,
             grind,
             special,
-            attributes: [attr1, attr2],
+            attributes: [attr1, attr2, None],
         }
     }
 
diff --git a/tests/test_shops.rs b/tests/test_shops.rs
index ade8ccf..831109c 100644
--- a/tests/test_shops.rs
+++ b/tests/test_shops.rs
@@ -211,7 +211,7 @@ async fn test_player_buys_multiple_from_tool_shop() {
 
     let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
     let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
-    assert!(c1.meseta < 999999);
+    assert_eq!(c1.meseta, 999749);
     let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
     assert_eq!(p1_items.items.len(), 1);
     p1_items.items[0].with_stacked(|item| {
@@ -259,7 +259,51 @@ async fn test_player_buys_from_armor_shop() {
 }
 
 #[async_std::test]
-async fn test_player_sells_to_shop() {
+async fn test_player_sells_3_attr_weapon_to_shop() {
+    let mut entity_gateway = InMemoryGateway::default();
+
+    let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
+
+    let mut p1_inv = Vec::new();
+
+    p1_inv.push(entity_gateway.create_item(
+        item::NewItemEntity {
+            item: item::ItemDetail::Weapon(
+                item::weapon::Weapon {
+                    weapon: item::weapon::WeaponType::Vulcan,
+                    grind: 5,
+                    special: Some(item::weapon::WeaponSpecial::Charge),
+                    attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
+                    tekked: true,
+                }
+            ),
+            location: item::ItemLocation::Inventory {
+                character_id: char1.id,
+            }
+        }).await.unwrap());
+
+    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
+
+    let mut ship = Box::new(ShipServerState::builder()
+        .gateway(entity_gateway.clone())
+        .build());
+
+    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
+    join_lobby(&mut ship, ClientId(1)).await;
+    create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
+
+    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
+        client: 0,
+        target: 0,
+        item_id: 0x10000,
+        amount: 1,
+    })))).await.unwrap().for_each(drop);
+
+    let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
+    let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
+    assert_eq!(c1.meseta, 4406);
 }
 
 #[async_std::test]
@@ -583,3 +627,146 @@ async fn test_units_disappear_from_shop_when_bought() {
         }).unwrap();
     }).unwrap();
 }
+
+#[async_std::test]
+async fn test_selling_untekked_weapon() {
+    let mut entity_gateway = InMemoryGateway::default();
+
+    let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
+
+    let mut p1_inv = Vec::new();
+
+    p1_inv.push(entity_gateway.create_item(
+        item::NewItemEntity {
+            item: item::ItemDetail::Weapon(
+                item::weapon::Weapon {
+                    weapon: item::weapon::WeaponType::Vulcan,
+                    grind: 5,
+                    special: Some(item::weapon::WeaponSpecial::Charge),
+                    attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
+                    tekked: false,
+                }
+            ),
+            location: item::ItemLocation::Inventory {
+                character_id: char1.id,
+            }
+        }).await.unwrap());
+
+    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
+
+    let mut ship = Box::new(ShipServerState::builder()
+        .gateway(entity_gateway.clone())
+        .build());
+
+    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
+    join_lobby(&mut ship, ClientId(1)).await;
+    create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
+
+    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
+        client: 0,
+        target: 0,
+        item_id: 0x10000,
+        amount: 1,
+    })))).await.unwrap().for_each(drop);
+
+    let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
+    let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
+    assert_eq!(c1.meseta, 1);
+}
+
+#[async_std::test]
+async fn test_player_sells_rare_item() {
+    let mut entity_gateway = InMemoryGateway::default();
+
+    let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
+
+    let mut p1_inv = Vec::new();
+
+    p1_inv.push(entity_gateway.create_item(
+        item::NewItemEntity {
+            item: item::ItemDetail::Weapon(
+                item::weapon::Weapon {
+                    weapon: item::weapon::WeaponType::DarkFlow,
+                    grind: 5,
+                    special: None,
+                    attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
+                            Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
+                    tekked: true,
+                }
+            ),
+            location: item::ItemLocation::Inventory {
+                character_id: char1.id,
+            }
+        }).await.unwrap());
+
+    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
+
+    let mut ship = Box::new(ShipServerState::builder()
+        .gateway(entity_gateway.clone())
+        .build());
+
+    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
+    join_lobby(&mut ship, ClientId(1)).await;
+    create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
+
+    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
+        client: 0,
+        target: 0,
+        item_id: 0x10000,
+        amount: 1,
+    })))).await.unwrap().for_each(drop);
+
+    let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
+    let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
+    assert_eq!(c1.meseta, 10);
+}
+
+#[async_std::test]
+async fn test_player_sells_photon_drops() {
+    let mut entity_gateway = InMemoryGateway::default();
+
+    let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
+
+    let mut p1_inv = Vec::new();
+
+    let mut photon_drops = Vec::new();
+    for _ in 0..7usize {
+        photon_drops.push(entity_gateway.create_item(
+            item::NewItemEntity {
+                item: item::ItemDetail::Tool(
+                    item::tool::Tool {
+                        tool: item::tool::ToolType::PhotonDrop,
+                    }
+                ),
+                location: item::ItemLocation::Inventory {
+                    character_id: char1.id,
+                }
+            }).await.unwrap());
+    }
+
+    p1_inv.push(item::InventoryItemEntity::Stacked(photon_drops));
+
+    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
+
+    let mut ship = Box::new(ShipServerState::builder()
+        .gateway(entity_gateway.clone())
+        .build());
+
+    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
+    join_lobby(&mut ship, ClientId(1)).await;
+    create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
+
+    ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSoldItem(PlayerSoldItem {
+        client: 0,
+        target: 0,
+        item_id: 0x10000,
+        amount: 3,
+    })))).await.unwrap().for_each(drop);
+
+    let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
+    let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
+    assert_eq!(c1.meseta, 3000);
+}
\ No newline at end of file