Closed polymorpher closed 2 years ago
re: withdraw - I agree with both
re: send - I agree with (1) but not entirely for (2). The function should be called by the operator instead of the owner. The permissions related to the owner and the operator should be separate. We may want to reserve the use of owner account for high-risk operations only (e.g. upgrading the contract, pause all activities, global per-user mini-wallet limit, and others)
re: transfer - we should use approval only instead of having the users to transfer the ownership over the token to the contract. The latter approach (transfer ownership) would make the token disappear from the user's list of owned tokens, which is not what the user wants.
Based on the feedback here and in the code review for https://github.com/polymorpher/sms-wallet/pull/1 I've added the following additional tasks (requirements)
Design Notes Add global per-user mini-wallet limit(owner function) GLOBAL AUTH LIMIT 100 (max they can authorize per destination)
uint256 public globalUserAuthLiimit; //Maximum a user can authorize
Add user limits for destination addresses (not just a global limit) Implementation Options
Use two-level mapping as suggested by @polymorpher below example from ERC20 is here
mapping(address => mapping(address => uint256)) private _allowances;
Key for mapping containing both sender and Recipient mapped to an amount
mapping(string => uint256) public userAuthorizations;
sample data
ALICE-GAMEA -> 10
ALICE-GAMEB -> 20
ALICE-GAMEC -> 30
Key contains sender and maps to an array of structure containing [recipient and amount]
struct auths {
address recipient;
uint256 amount;
}
mapping(address => auths[]) public userAuthorizations;
sample data
ALICE -> [[GAMEA,10],[GAMEB,20],[GAMEC,30]]
Recommendation is to go with option 0.
Previous recommendation of option 2 is overly complicated and inefficient, thanks @polymorpher
Although this complicates the storage and retrieval of the authorization. It has the benefit of being able to retrieve all authorizations for a sender. Which is useful if when we need to check total_authorizations for a sender.
Gist for option 2 (outdated) uses a mapping (so that we can look up the users quickly and an array of arrays to store the recipient limits)
struct RecipientAuth {
address recipient;
uint256 limit;
}
struct UserRecipientAuth {
address user;
RecipientAuth[] recipientAuths;
}
UserRecipientAuth[] userRecipientAuths;
mapping(address => uint256) public userBalances;
mapping(address => uint) public userAuthorizations;
Error with first Attempt at option 2 This was using a mapping storing an array of recipient values FirstDraft of AssetManager.sol Compile error gist
struct userAuth {
address recipient;
uint256 limit;
}
userAuth[] userAuths;
mapping(address => uint256) public userBalances;
mapping(address => userAuth[]) public userAuthorizations;
References Reference Mapping Types Solidity Documentation Reference Question multi attribute query StakeOverflow Reference Question multi value mappings StackOverflow Reference Question memory vs storage StackOverflow
Have you considered using two-level mapping? It is common in ERC20,721,1155 contract templates. It is a lot more efficient than using a single level mapping to an array
The first error you had is because you were creating a temporary storage variable, which does not make sense because storage variables are meant to be persisted. You need to change that to a memory variable. To resolve the second error you need to copy the elements one by one and extend the storage array if needed.
Design Notes
TODO
I think we can close this off for now. ERC777 is not a priority right now
I will add a note to update the wiki later per this discussion and actual implementations
Below comments are submitted by John in the initial full-spec Google Doc (which got moved to Wiki). I am consolidating them here for further discussions
Re: mini-wallet, withdraw