Some users might like optional, more granular permissions, i.e. what actions is allowed or not.
Yeah, that’s a good point. If we have methods taking batch of actions, then it makes sense to explicitly grant permissions for action types as well, for more secure permissions settings
Though, I’m curious how you envision an ideal confirmation flow within the single popup. Does user still click multiple confirmation buttons for each/some of the items or it’s just one-click maybe with radio buttons for each item, etc…?
By radio buttons do you mean making users explicitly confirm each action separately, or an ability to keep some actions unconfirmed?
Asking users to explicitly confirm each action separately would be definitely more secure, but at the same time annoying So, we need to find some balance.
As for allowing some actions to be unconfirmed, not sure if it makes sense, because a dapp actually shouldn’t request actions, which are unnecessary. So, if all actions are necessary, then having at least one rejected action will mean a rejected batch.
Right now we have a single confirmation window, where we display the whole list of actions a dapp requested to execute and a single confirmation button, so a user checks the whole batch and confirms all or nothing by a single click.
- Action batching: While I understand the UX benefits of batching actions, I see some potential issues:
- How do we handle partial failures in a batch?
If we speak about how it should work, then if a batch fails at some action, then all the previous actions must be reverted and all the following actions must be skipped. But right now it’s not possible, because PXE doesn’t provide remove_*
methods, so you cannot revert adding a contract, for example.
Anyway, even without reverting, it will work well. If you have a batch [add_contract, call] failed on the call due to wrong parameters, for example, and the dapp sends it again after adjusting the parameters, you will just add the contract again, that won’t break anything (btw, you can check if you’ve already added the contract, and if so, simply ignore the action).
- What does it mean to simulate non-Call actions?
Most non-Call actions can be needed for a Call action to succeed, so they can be a necessary part of the simulation. For instance, if you have a contract method that consumes a capsule or a note, then you will have to add it before simulation, otherwise the call will fail. Moreover, capsules are removed when consumed, so you will have to add it each time.
If I don’t miss anything, only add_contact
action is never needed for execution/simulation directly, so its simulation indeed doesn’t make any sense. Also it doesn’t make sense to simulate a batch consisting of non-Call actions only. So, there are two options what we could do:
- restrict meaningless actions or combination of actions in the
simulate
method, making it more complicated for dapp devs;
- or allow passing any actions to provide the same UX as with
execute
, but in case of meaningless actions simply do nothing on the wallet side and just respond with OK.
- Different types of actions may need different handling.
Why do you think that is an issue? With method-per-action approach the same different handling will be needed.
- Combining unrelated actions might actually worsen the UX (from an end-user confusion standpoint) rather than improve it.
That should be dapp’s responsibility to follow best practices and avoid batching unrelated actions, to not confuse their users when they are asked to approve it.
I propose splitting this into two concepts:
- Transaction batching (for related blockchain operations like approve+swap)
- Individual operations for other distinct wallet interactions (contract registration, etc.)
That approach totally makes sense. However, besides forcing dapp users to do multiple confirmations, there is another issue.
One of the main ideas of batching is to have atomic-ish execution, which is necessary, for example, when working with capsules. PXE stores capsules in global stack, so if you call a contract that is going to consume a capsule, you must ensure that between adding the capsule and calling the contract no other capsules were pushed and no other contracts popped it.
It’s easy to achieve with batch execution, and problematic with independent workflows for adding and consuming capsules.
Transaction Parameter Templates
That’s a very interesting idea
This would allow dapps to work without requiring account addresses by default, and providing account addresses can be opt-in instead of mandatory
Yeah, that would make sense if we had the parameter templates feature standardized.
The connect response could include more metadata about the connection
Yeah, I actually skipped such details to focus more on the wallet interface instead. So, the connect
method indeed should return a session, containing:
- a session id (which is used further to authorize dapp requests);
- granted permissions (because the
connect
method actually takes two parameters: requiredPermissions
and optionalPermissions
, so the dapp should know which optional permissions were approved);
- shared accounts (we use CAIP-10, that is
"{chain_id}:{address}"
strings).
Btw, short question: do we want to care about session hijacking, or it’s not worth it? If yes, we would also need to introduce something like signature-based authorization for dapp requests.
Permissions and Meta-operations
- How to handle permission changes mid-session
That’s a good question. There are actually two different cases:
- Dapp wants to adjust permissions. In this case either the dapp can simply reconnect with the new permissions, or we can have another method in the wallet interface, like
upgradeSession(newPermissions)
. No big difference, IMO
- User wants to adjust permissions. For instance, he wants to share one more account, or revoke some of the previously shared accounts. In this case the wallet should notify the dapp about changes somehow. This can be done, for instance, by producing a
session_changed
event. However, still not sure what to do if the dapp is “offline” at that moment. Perhaps dapps should be able to get info about the active session.
- How to handle capability discovery
I’d propose to handle it by the permissions functionality. As I mentioned above, there is the optionalPermissions
parameter, that could be used to request/discover additional features supported by a dapp.
Simple example,
const requiredPermissions: [
"execute 'call' action on mainnet"
];
const optionalPermissions: [
"allow template parameters"
];
await connect(dapp, requiredPermissions, optionalPermissions);
Roughly speaking, a dapp requests permissions for particular functionalities, and the wallet responds with a subset, that is supported by the wallet and approved by the user.
In this case, the getCapabilities()
method doesn’t make much sense, because a dapp doesn’t really care about all wallet’s capabilities, but only about specific ones (supported by the dapp), so the dapp can simply request them via optional permissions.
Are folks OK with starting on JSON-RPC layer definitions and then moving on to higher-level abstractions?
Actually, JSON-RPC spec is the “higher-level abstraction”, because it describes a particular implementation of a base interface, determining wallet’s functionality and behavior So, IMO, it’s more correct to have a base interface first, and then talk about particular implementations, like JSON-RPC spec, gRPC spec, etc…
But I don’t insist It’s not something that I want to argue about.