From f7362b7778deb0174a53fedd1f65480d2b7ac585 Mon Sep 17 00:00:00 2001 From: on a t break Date: Wed, 25 Sep 2024 19:03:35 -0700 Subject: [PATCH 1/2] dev-feature-300 --- include/atomicassets.hpp | 22 +++++++++++- src/atomicassets.cpp | 72 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/include/atomicassets.hpp b/include/atomicassets.hpp index 188d9d5..1868d20 100644 --- a/include/atomicassets.hpp +++ b/include/atomicassets.hpp @@ -33,6 +33,13 @@ CONTRACT atomicassets : public contract { string memo ); + ACTION move( + name owner, + name from, + name to, + vector asset_ids, + string memo + ); ACTION createcol( name author, @@ -269,6 +276,19 @@ CONTRACT atomicassets : public contract { typedef multi_index collections_t; + TABLE holders_s { + uint64_t asset_id; + name holder; + name owner; + + uint64_t primary_key() const { return asset_id; }; + uint64_t by_holder() const { return holder.value; }; + }; + + typedef multi_index >> + holders_t; + //Scope: collection_name TABLE schemas_s { name schema_name; @@ -312,7 +332,6 @@ CONTRACT atomicassets : public contract { typedef multi_index assets_t; - TABLE offers_s { uint64_t offer_id; name sender; @@ -365,6 +384,7 @@ CONTRACT atomicassets : public contract { collections_t collections = collections_t(get_self(), get_self().value); + holders_t holders = holders_t(get_self(), get_self().value); offers_t offers = offers_t(get_self(), get_self().value); balances_t balances = balances_t(get_self(), get_self().value); config_t config = config_t(get_self(), get_self().value); diff --git a/src/atomicassets.cpp b/src/atomicassets.cpp index 19514ba..43d1ad2 100644 --- a/src/atomicassets.cpp +++ b/src/atomicassets.cpp @@ -1,6 +1,5 @@ #include - /** * Initializes the config table. Only needs to be called once when first deploying the contract * @required_auth The contract itself @@ -81,6 +80,77 @@ ACTION atomicassets::transfer( internal_transfer(from, to, asset_ids, memo, from); } +/** +* Moves one or more assets to another account +* @required_auth of the true owner of the asset +* Cannot have notifications for the from & to, exploitable +*/ +ACTION atomicassets::move( + name owner, + name from, + name to, + vector asset_ids, + string memo +) { + require_auth(owner); + require_recipient(owner); + + check(is_account(from), "from account does not exist"); + check(is_account(to), "to account does not exist"); + + check(from != to, "from & to fields cannot be the same"); + + check(asset_ids.size() != 0, "asset_ids needs to contain at least one id"); + check(memo.length() <= 256, "A move memo can only be 256 characters max"); + + vector asset_ids_copy = asset_ids; + std::sort(asset_ids_copy.begin(), asset_ids_copy.end()); + check(std::adjacent_find(asset_ids_copy.begin(), asset_ids_copy.end()) == asset_ids_copy.end(), + "Can't move the same asset multiple times"); + + assets_t owner_assets = get_assets(owner); + + for (uint64_t & asset_id : asset_ids) { + auto asset_itr = owner_assets.require_find(asset_id, + ("Owner doesn't own at least one of the provided assets (ID: " + + to_string(asset_id) + ")").c_str()); + + //Existence doesn't have to be checked because this always has to exist + if (asset_itr->template_id >= 0) { + templates_t collection_templates = get_templates(asset_itr->collection_name); + + auto template_itr = collection_templates.find(asset_itr->template_id); + check(template_itr->transferable, + ("At least one asset isn't transferable (ID: " + to_string(asset_id) + ")").c_str()); + } + + auto holders_itr = holders.find(asset_id); + if (holders_itr == holders.end()){ + check(from == owner, + ("Only the owner can move this asset (ID: " + to_string(asset_id) + ")").c_str()); + // Emplaces new holder + holders.emplace(owner, [&](auto &_holders_row){ + _holders_row.asset_id = asset_id; + _holders_row.holder = to; + _holders_row.owner = owner; + }); + } + + if (holders_itr != holders.end()){ + check(holders_itr->holder == from, + ("At least one asset invalidates the 'from:holder' constraint (ID: " + to_string(asset_id) + ")").c_str()); + + // Deletes row if returning to owner + if (to == owner){ + holders.erase(holders_itr); + } else { // Modifies row to move holdership to the new "to" wallet + holders.modify(holders_itr, owner, [&](auto &_holders_row){ + _holders_row.holder = to; + }); + } + } + } +} /** * Creates a new collection From 86bfff433f285e0bbaccc6cc28baa694f1ee316a Mon Sep 17 00:00:00 2001 From: on a t break Date: Wed, 2 Oct 2024 22:16:57 -0700 Subject: [PATCH 2/2] dev-feature-300 --- src/atomicassets.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/atomicassets.cpp b/src/atomicassets.cpp index 43d1ad2..16b371c 100644 --- a/src/atomicassets.cpp +++ b/src/atomicassets.cpp @@ -1338,6 +1338,19 @@ void atomicassets::internal_transfer( ("At least one asset isn't transferable (ID: " + to_string(asset_id) + ")").c_str()); } + auto holders_itr = holders.find(asset_id); + if (holders_itr != holders.end()){ + + // Deletes row if transfering to holder + if (to == holders_itr->holder){ + holders.erase(holders_itr); + } else { // Modifies row to move ownership to the new "to" wallet + holders.modify(holders_itr, from, [&](auto &_holders_row){ + _holders_row.owner = to; + }); + } + } + //This is needed for sending notifications later if (collection_to_assets_transferred.find(asset_itr->collection_name) != collection_to_assets_transferred.end()) {