Recently we did a competition on SiNEAR_EA_* repo, and there are some idea on optimization.
First, we have quite a lot of repeats in
Contract interface, including
tokens_by_owner_ordered. We could remove the former if we need an ordered version like the latter. Easy stuff.
How to store the ordered version is another problem. Currently, we’re using
HashMap for storage, where the key is a numerical character (of type
u16) and the value the
TokenId. The numerical character are mapped to
pub categories in
Contract, which is a
Vector containing the category names. This way, we don’t have to store the category name twice.
Storing the number in
u16 might be more cost efficient than storing
There’s a time one thought of not using
Vec. However, if owner only donate to one field for the rest of their lives, and we have 10 fields available for donations, 9 of 10 of the fields stores an empty String, which is just a waste of place.
Vec is more storage efficient if and only if the people
donate to more than half of the categories, which isn’t true most of the time. People usually have their specific “cause areas” (more info on this term please learn about Effective Altruism), not simply donate. Oneself used to donate to “maximum impact” all the time.
Then goes the communication between backend and frontend; how do we make a view functions so frontend don’t have to deal with what backend has? We solved this in the code, go check it out! Basically, we just take values from the view function and returns them (in random order) to frontend; and frontend deals with donations also in “random order”.
Currently, we have a
donate_and_update as callback from
minting_interface, which have their own callbacks. Each of these takes about 15-20 TGas (depending on how much args are passed in and the function
calculation complexity); which just isn’t ideal. Cross contract calls seems to be expensive? I don’t know. Hence, we want to reduce cross-contract calls if that’s true.
First, we don’t need
donate_and_update as a contract function, but could be internal functions. We can deal with everything (except the Promises to call
mint and perhaps callbacks) internally, hence no need to perform cross contract calls, in one go. Then only we call
mint via cross-contract calls as it requires.
Second, we don’t need to call
Promise::new(env::signer_account_id()).transfer(money) for each of
donate_and_update. We could just minus the total once, but store how much money people donate separately. That way, calling
Promise only once saves lots of gas! The only exception? When the donation goes to different accounts for each of the category, then we have no choice. If they all go to contract account, don’t call them separately.
Minting have to do separately, one afraid; though there might be solution for mass minting? I don’t know.
Then how metadata is stored. For those that have mostly the same metadata, especially generating from a template, they don’t need to store their own metadata in a
LookupMap. Instead, we could replace it with a pointer pointing to a template for metadata. If there is a small difference like
issued_at, we could store that separately, but just for that particular field, not the whole metadata.
By pointing to the template, we’re saving unnecessary space from being used, and pull metadata directly from template + extra differences (that worth if storing separately for each
TokenId than storing the whole Metadata), aggregate them in the view functions before pushing to frontend for user to view.
That’s all one could think for now! Stay tuned.