Account delegation
Account delegation allows a delegator address to permit a delegatee address to call a System
on its behalf.
Alternatively, the namespace owner can register a delegation that applies to all calls to all System
s in the namespace.
This feature enables multiple use cases.
-
Session wallets. Normally a wallet requires the user to authorize each transaction separately. In the case of a blockchain game, this means having to authorize every move, which is excessive. Using account delegation players can authorize a different wallet, one whose private key is stored on the client, to act on their behalf. Because this wallet's private key is stored in the client, rather than a browser extension, the client can decide when asking for authorization is warranted and when it isn't. By making sure this is a separate wallet, we protect the player's main account in the case of vulnerable or malicious game clients.
-
Approvals. Users can approve contracts to perform actions in a MUD world on their behalf. This is conceptually similar to how ERC20's
approve
/transferFrom
(opens in a new tab) enables atomic swaps by allowing contracts to withdraw from a user's balance and then deposit a different asset in a single transaction. For example, an agent could exchange two in-game assets atomically if the two sides of the trade give it the necessary permission.
User delegation
The most common type of delegation is when a delegator address allows a specific delegatee address to act on its behalf.
Creating a user delegation
First, the delegator has to call registerDelegation
(opens in a new tab).
This function takes three parameters:
-
delegatee
, the address given the privileges. This can beaddress(0)
to let this delegation apply to all callers. Note that combiningaddress(0)
with an unlimited delegation is discouraged, as it would allow anyone to perform any action on behalf of the delegator. -
delegationControlId
, this is usually theResourceId
for aSystem
that decides whether an attempt to do something by the delegatee on behalf of the delegator is authorized or not. Alternatively, this value can beUNLIMITED_DELEGATION
(opens in a new tab), in which case the delegatee has unlimited authority.We have an example of a delegation control
System
in thestd-delegations
module (opens in a new tab). -
initCallData
, call data for a function that is called on thedelegationControlId
to inform it of the new delegation. This call data (opens in a new tab) includes both the function selector of the function to call and arguments to pass to the function (the result ofabi.encodeCall
).
Using a user delegation
The delegatee can use callFrom
(opens in a new tab).
This function takes three parameters:
delegator
, the address on whose behalf the call is happening.systemId
, theSystem
to call.callData
, the call data (opens in a new tab) to send theSystem
, which includes both the function selector and arguments (the result ofabi.encodeCall
).
Note that between a specific delegator and a specific delegatee there can only be one user delegation at a time.
This means that if you need in a World
to implement multiple delegation algorithms you might need to create a dispatcher delegation that calls different verification functions based on the systemId
and callData
it receives.
Removing a user delegation
You can use unregisterDelegation
(opens in a new tab) to remove a delegation.
Because of unregisterDelegation
delegations cannot be used for a delegator to commit to allow something to be done in the future.
If you need a commitment, create a table with a System
that lets the address that commits write the commitment and have an action that other addresses can call only if the proper commitment is there - without giving the committed address an option to delete the entry.
Namespace delegation
The owner of a namespace can use registerNamespaceDelegation
(opens in a new tab) to register a delegation that applies to all callers of systems in this namespace.
This functionality is useful, for example, to implement a trusted forwarder for the namespace.
This is not a security concern because the namespace owner already has full control over any table or System
in the namespace.
Order of delegation checks
As you can see in callFrom
(opens in a new tab), the order of delegation checks is:
- If there is a
world:UserDelegationControl
entry with the delegator and the delegatee (a.k.a.msg.sender
), check it. If it results in an approval, perform the call. - If the user provided a fallback delegation (one where the delegatee is
address(0)
), check it. If it results in an approval, perform the call. - If there is an applicable namespace delegation, check that one. If it results in an approval, perform the call.
This means that if there's a namespace delegation that modifies information (for example, writes to a MUD table to gather statistics) users will be able to bypass it by creating their own delegation with the same delegetee and a System
that would approve.