-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev feature 3 + 6 - Mutable Templates & Non-Burnable or Non-transferable XOR condition #8
base: master
Are you sure you want to change the base?
Changes from 6 commits
6075419
86b4708
4ff8b26
35dbb88
d4857de
4d69ac0
e7a3241
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 |
---|---|---|
|
@@ -104,6 +104,17 @@ CONTRACT atomicassets : public contract { | |
ATTRIBUTE_MAP immutable_data | ||
); | ||
|
||
ACTION createtempl2( | ||
name authorized_creator, | ||
name collection_name, | ||
name schema_name, | ||
bool transferable, | ||
bool burnable, | ||
uint32_t max_supply, | ||
ATTRIBUTE_MAP immutable_data, | ||
ATTRIBUTE_MAP mutable_data | ||
); | ||
|
||
ACTION locktemplate( | ||
name authorized_editor, | ||
name collection_name, | ||
|
@@ -129,6 +140,13 @@ CONTRACT atomicassets : public contract { | |
ATTRIBUTE_MAP new_mutable_data | ||
); | ||
|
||
ACTION settempldata( | ||
name authorized_editor, | ||
name collection_name, | ||
name schema_name, | ||
int32_t template_id, | ||
ATTRIBUTE_MAP new_mutable_data | ||
); | ||
|
||
ACTION announcedepo( | ||
name owner, | ||
|
@@ -233,6 +251,14 @@ CONTRACT atomicassets : public contract { | |
ATTRIBUTE_MAP new_data | ||
); | ||
|
||
ACTION logsetdatatl( | ||
name collection_name, | ||
name schema_name, | ||
int32_t template_id, | ||
ATTRIBUTE_MAP old_data, | ||
ATTRIBUTE_MAP new_data | ||
); | ||
|
||
ACTION logbackasset( | ||
name asset_owner, | ||
uint64_t asset_id, | ||
|
@@ -295,6 +321,16 @@ CONTRACT atomicassets : public contract { | |
|
||
typedef multi_index <name("templates"), templates_s> templates_t; | ||
|
||
//Scope: collection_name | ||
TABLE template_data_s { | ||
int32_t template_id; | ||
name schema_name; | ||
vector <uint8_t> mutable_serialized_data; | ||
|
||
uint64_t primary_key() const { return (uint64_t) template_id; } | ||
}; | ||
|
||
typedef multi_index <name("templatedata"), template_data_s> template_data_t; | ||
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. I think calling this templatedata could be confusing, because template data already exists as part of the normal template table. A name that makes it clear this is referring to mutable template data would be better imo. 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. I agree, wdy think about these:
|
||
|
||
//Scope: owner | ||
TABLE assets_s { | ||
|
@@ -370,6 +406,16 @@ CONTRACT atomicassets : public contract { | |
config_t config = config_t(get_self(), get_self().value); | ||
tokenconfigs_t tokenconfigs = tokenconfigs_t(get_self(), get_self().value); | ||
|
||
void create_template( | ||
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. I would call this |
||
name & authorized_creator, | ||
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. I am not a fan of passing names by reference, since they're essentially just 64 bit primitive types. Most likely compiles to exactly the same output anyways, so more of a nitpick than anything else. 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. Sure, can be set to pass by value. I always do everything by reference when I can if I know that the original isn't going to be mutated. |
||
name & collection_name, | ||
name & schema_name, | ||
bool transferable, | ||
bool burnable, | ||
uint32_t max_supply, | ||
ATTRIBUTE_MAP & immutable_data, | ||
ATTRIBUTE_MAP mutable_data = {} | ||
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. is there a reason not to use a reference for the mutable data when you're using one for the immutable data? I also don't really like the default value, I'd rather see that explicitly when calling the function. 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. There's no reference for the mutable_data because the original createtempl action does not have a field for mutable_data (only immutable_data). It's only accessed by the new createtempl2 action. Just looks a bit cleaner this way, as the function doesn't have to create an empty ATTRIBUTE_MAP & pass that over (if it was by reference) |
||
); | ||
|
||
void internal_transfer( | ||
name from, | ||
|
@@ -408,4 +454,6 @@ CONTRACT atomicassets : public contract { | |
schemas_t get_schemas(name collection_name); | ||
|
||
templates_t get_templates(name collection_name); | ||
|
||
template_data_t get_template_data(name collection_name); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -409,6 +409,7 @@ ACTION atomicassets::extendschema( | |
* Creates a new template | ||
* @required_auth authorized_creator, who is within the authorized_accounts list of the collection | ||
*/ | ||
|
||
ACTION atomicassets::createtempl( | ||
name authorized_creator, | ||
name collection_name, | ||
|
@@ -417,53 +418,26 @@ ACTION atomicassets::createtempl( | |
bool burnable, | ||
uint32_t max_supply, | ||
ATTRIBUTE_MAP immutable_data | ||
) { | ||
require_auth(authorized_creator); | ||
|
||
auto collection_itr = collections.require_find(collection_name.value, | ||
"No collection with this name exists"); | ||
|
||
check_has_collection_auth( | ||
authorized_creator, | ||
collection_name, | ||
"The creator is not authorized within the collection" | ||
); | ||
|
||
schemas_t collection_schemas = get_schemas(collection_name); | ||
auto schema_itr = collection_schemas.require_find(schema_name.value, | ||
"No schema with this name exists"); | ||
|
||
config_s current_config = config.get(); | ||
int32_t template_id = current_config.template_counter++; | ||
config.set(current_config, get_self()); | ||
|
||
templates_t collection_templates = get_templates(collection_name); | ||
) { | ||
create_template(authorized_creator, collection_name, schema_name, transferable, burnable, max_supply, immutable_data); | ||
} | ||
|
||
collection_templates.emplace(authorized_creator, [&](auto &_template) { | ||
_template.template_id = template_id; | ||
_template.schema_name = schema_name; | ||
_template.transferable = transferable; | ||
_template.burnable = burnable; | ||
_template.max_supply = max_supply; | ||
_template.issued_supply = 0; | ||
_template.immutable_serialized_data = serialize(immutable_data, schema_itr->format); | ||
}); | ||
/** | ||
* Creates a new template with explicit mutable data fields | ||
* @required_auth authorized_creator, who is within the authorized_accounts list of the collection | ||
*/ | ||
|
||
action( | ||
permission_level{get_self(), name("active")}, | ||
get_self(), | ||
name("lognewtempl"), | ||
make_tuple( | ||
template_id, | ||
authorized_creator, | ||
collection_name, | ||
schema_name, | ||
transferable, | ||
burnable, | ||
max_supply, | ||
immutable_data | ||
) | ||
).send(); | ||
ACTION atomicassets::createtempl2( | ||
name authorized_creator, | ||
name collection_name, | ||
name schema_name, | ||
bool transferable, | ||
bool burnable, | ||
uint32_t max_supply, | ||
ATTRIBUTE_MAP immutable_data, | ||
ATTRIBUTE_MAP mutable_data | ||
) { | ||
create_template(authorized_creator, collection_name, schema_name, transferable, burnable, max_supply, immutable_data, mutable_data); | ||
} | ||
|
||
|
||
|
@@ -666,6 +640,85 @@ ACTION atomicassets::setassetdata( | |
}); | ||
} | ||
|
||
/** | ||
* Updates the mutable data of a template within the templatedata table | ||
* If the row doesn't exist within the template, it emplaces a new row | ||
* If the new_mutable_data is empty & the row exists, it eraes the row | ||
* @required_auth authorized_editor, who is within the authorized_accounts list of the collection | ||
specified in the related template | ||
*/ | ||
|
||
|
||
ACTION atomicassets::settempldata( | ||
name authorized_editor, | ||
name collection_name, | ||
name schema_name, | ||
int32_t template_id, | ||
ATTRIBUTE_MAP new_mutable_data | ||
) { | ||
|
||
require_auth(authorized_editor); | ||
|
||
auto collection_itr = collections.require_find(collection_name.value, | ||
"No collection with this name exists"); | ||
|
||
check_has_collection_auth( | ||
authorized_editor, | ||
collection_name, | ||
"The editor is not authorized within the collection" | ||
); | ||
|
||
schemas_t collection_schemas = get_schemas(collection_name); | ||
auto schema_itr = collection_schemas.require_find(schema_name.value, | ||
"No schema with this name exists"); | ||
|
||
templates_t collection_templates = get_templates(collection_name); | ||
auto template_itr = collection_templates.require_find(template_id, | ||
"No template with the specified id exists for the specified collection"); | ||
|
||
check_name_length(new_mutable_data); | ||
|
||
template_data_t template_data = get_template_data(collection_name); | ||
auto template_data_itr = template_data.find(template_id); | ||
|
||
ATTRIBUTE_MAP deserialized_old_data; | ||
|
||
if (template_data_itr != template_data.end()){ | ||
deserialized_old_data = deserialize( | ||
template_data_itr->mutable_serialized_data, | ||
schema_itr->format | ||
); | ||
} | ||
|
||
action( | ||
permission_level{get_self(), name("active")}, | ||
get_self(), | ||
name("logsetdatatl"), | ||
make_tuple(collection_name, schema_name, template_id, deserialized_old_data, new_mutable_data) | ||
).send(); | ||
|
||
// If entry doesn't exist, then emplace entry | ||
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. This control flow has a problem where you could call it with empty new mutable data, and this empty data would then be emplaced. Doesn't break anything, but I think the following controlflow would be better and also clearer to understand (in pseudocode):
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. Added a condition for emplacing. It's a rather unique scenario & would likely only be executed by accident by users, but this just makes it a bit cleaner internally & more consistent with the entry modification functionality, while still not throwing an error to the user & breaking the transaction. I'm leaning towards not having a check here to avoid breaking transactions when it's not critical / absolutely necessary. if (template_data_itr == template_data.end() && new_mutable_data.size() > 0){
template_data.emplace(authorized_editor, [&](auto &_template_data) {
_template_data.template_id = template_id;
_template_data.schema_name = template_itr->schema_name;
_template_data.mutable_serialized_data = serialize(new_mutable_data, schema_itr->format);
});
} |
||
if (template_data_itr == template_data.end()){ | ||
template_data.emplace(authorized_editor, [&](auto &_template_data) { | ||
_template_data.template_id = template_id; | ||
_template_data.schema_name = schema_name; | ||
_template_data.mutable_serialized_data = serialize(new_mutable_data, schema_itr->format); | ||
}); | ||
} | ||
|
||
// If entry exists && new_mutable_data is not empty, then modify entry | ||
if (template_data_itr != template_data.end() && new_mutable_data.size() > 0){ | ||
template_data.modify(template_data_itr, authorized_editor, [&](auto &_template_data) { | ||
_template_data.mutable_serialized_data = serialize(new_mutable_data, schema_itr->format); | ||
}); | ||
} | ||
|
||
// If entry exists && new_mutable_data is empty, then erase entry | ||
if (template_data_itr != template_data.end() && new_mutable_data.size() == 0){ | ||
template_data.erase(template_data_itr); | ||
} | ||
|
||
} | ||
|
||
/** | ||
* This action is used to add a zero value asset to the quantities vector of owner in the balances table | ||
|
@@ -1187,6 +1240,17 @@ ACTION atomicassets::logsetdata( | |
notify_collection_accounts(asset_itr->collection_name); | ||
} | ||
|
||
ACTION atomicassets::logsetdatatl( | ||
name collection_name, | ||
name schema_name, | ||
int32_t template_id, | ||
ATTRIBUTE_MAP old_data, | ||
ATTRIBUTE_MAP new_data | ||
) { | ||
require_auth(get_self()); | ||
|
||
notify_collection_accounts(collection_name); | ||
} | ||
|
||
ACTION atomicassets::logbackasset( | ||
name asset_owner, | ||
|
@@ -1221,6 +1285,96 @@ ACTION atomicassets::logburnasset( | |
} | ||
|
||
|
||
/** | ||
* Function for creating a template, handling both the creation of normal templates & purely mutable templates | ||
*/ | ||
|
||
void atomicassets::create_template( | ||
name & authorized_creator, | ||
name & collection_name, | ||
name & schema_name, | ||
bool transferable, | ||
bool burnable, | ||
uint32_t max_supply, | ||
ATTRIBUTE_MAP & immutable_data, | ||
ATTRIBUTE_MAP mutable_data | ||
) { | ||
require_auth(authorized_creator); | ||
|
||
auto collection_itr = collections.require_find(collection_name.value, | ||
"No collection with this name exists"); | ||
|
||
check_has_collection_auth( | ||
authorized_creator, | ||
collection_name, | ||
"The creator is not authorized within the collection" | ||
); | ||
|
||
schemas_t collection_schemas = get_schemas(collection_name); | ||
auto schema_itr = collection_schemas.require_find(schema_name.value, | ||
"No schema with this name exists"); | ||
|
||
config_s current_config = config.get(); | ||
int32_t template_id = current_config.template_counter++; | ||
config.set(current_config, get_self()); | ||
|
||
if (transferable == false){ | ||
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. I'd prefer 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. clean |
||
check(burnable == true, | ||
"A template cannot be both non-transferable and non-burnable"); | ||
} | ||
|
||
templates_t collection_templates = get_templates(collection_name); | ||
|
||
check_name_length(immutable_data); | ||
collection_templates.emplace(authorized_creator, [&](auto &_template) { | ||
_template.template_id = template_id; | ||
_template.schema_name = schema_name; | ||
_template.transferable = transferable; | ||
_template.burnable = burnable; | ||
_template.max_supply = max_supply; | ||
_template.issued_supply = 0; | ||
if (immutable_data.size() > 0){ | ||
_template.immutable_serialized_data = serialize(immutable_data, schema_itr->format); | ||
} | ||
}); | ||
|
||
action( | ||
permission_level{get_self(), name("active")}, | ||
get_self(), | ||
name("lognewtempl"), | ||
make_tuple( | ||
template_id, | ||
authorized_creator, | ||
collection_name, | ||
schema_name, | ||
transferable, | ||
burnable, | ||
max_supply, | ||
immutable_data | ||
) | ||
).send(); | ||
|
||
if (mutable_data.size() > 0){ | ||
check_name_length(mutable_data); | ||
|
||
template_data_t template_data = get_template_data(collection_name); | ||
template_data.emplace(authorized_creator, [&](auto &_template_data) { | ||
_template_data.template_id = template_id; | ||
_template_data.schema_name = schema_name; | ||
_template_data.mutable_serialized_data = serialize(mutable_data, schema_itr->format); | ||
}); | ||
|
||
action( | ||
permission_level{get_self(), name("active")}, | ||
get_self(), | ||
name("logsetdatatl"), | ||
make_tuple(collection_name, schema_name, template_id, mutable_data, mutable_data) | ||
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. old data in the log should be empty, not the mutable data as well. |
||
).send(); | ||
} | ||
|
||
} | ||
|
||
|
||
/** | ||
* Transfers need to be handled like this (as a function instead of an action), because when accepting an offer, | ||
* we want each side of the offer to pay for their own scope. Because the recipient authorized the accept action, | ||
|
@@ -1482,4 +1636,8 @@ atomicassets::schemas_t atomicassets::get_schemas(name collection_name) { | |
|
||
atomicassets::templates_t atomicassets::get_templates(name collection_name) { | ||
return templates_t(get_self(), collection_name.value); | ||
} | ||
|
||
atomicassets::template_data_t atomicassets::get_template_data(name collection_name) { | ||
return template_data_t(get_self(), collection_name.value); | ||
} |
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.
I don't think schema_name should be a part of the action signature, as it can be inferred from the template_id.
setassetdata
also doesn't include the schema name