Boop Extensions
Boop supports two kind of extensions: validation extensions and execution extensions.
- Validation extensions are triggered at the account's discretion in the
validate(Boop)
function. They enable permissioning certain operations differently. Validation extensions implement their own version ofvalidate(Boop)
, with the same signature and specification.
- Execution extensions are triggered at the account's discretion in the
execute(Boop)
function. They enable customizing how certain operations are run. Execution extensions implement their own version ofexecution(Boop)
, with the same signature and specification.
A typical account implementation like the HappyAccount
will have a standard validation and execution path,
usually verifying some kind of signature for validation and carrying off the call specified in the boop's dest
,
callData
and value
fields for execution. Extensions allow it to do different things in certain cases.
We currently maintain two extensions:
-
The
SessionKeyValidator
enables account to authorize transactions to specific addresses to be signed by another EOA. We use this to implement session keys in the Happy Wallet, enabling a user to authorize an app to make transactions on their behalf, but the principle can be using for any kind of simple delegation with a given target address as the singular criteria. -
The
BatchCallExecutor
enables batching multiple contract calls or transfers into a single Boop. It's the equivalent of a multicall, but more powerful as themsg.sender
on these calls will be account itself, just like for a normal Boop.
See the sections below on using the SessionKeyValidator
and using the
BatchCallExecutor
.
Accepting Extensions
To support extensions, an account must implement IExtensibleAccount
instead of IAccount
(which it extends).
See the HappyAccount
for an example of this.
The interface adds a few functions to implement:
addExtension(address extension, ExtensionType type, bytes installData)
removeExtension(address extension, ExtensionType type, bytes uninstallData)
isExtensionRegistered(address extension, ExtensionType type)
executeCallFromExecutor(CallInfo)
The first three are pretty self-explanatory, this is how you add, remove and query extensions on the account. Each
extension has its own deployed contract. The type
here is an enum that indicates whether the extension is an execution
or validation extension, and could be extended with other types in the future. If not empty, the data parameter is used
to make a call to the extension contract, which can be used for some install/uninstall bookkeeping, or to bundle
installation with initial usage. For instance, we use it to register an initial session key at the same time that we
install the session key extension.
Note that these first three functions can be called as boops themselves. Simply fire off a boop that makes
a call to the account itself, and don't forget the lock down who can call the function. The HappyAccount
enables
them to be called as boops or from the owner EOA directly via the onlySelfOrOwner
modifier.
The executeCallFromExecutor
function enables execution extensions to make calls on behalf of the account (i.e.
msg.sender
= account address). It must be locked down to only enable calls from an authorized executor, which we
recommend implementing via a transient
variable set during the execute
call. The CallInfo
struct is just the usual
dest
, callData
, value
triplet.
Dispatching Extensions
How to know when to use an extension?
The account's validate
and execute
functions can use any criteria they wants, however that would
have to be hardcoded in the logic.
The standard & recommended way to dispatch an extension is to use a reserved key in the boop's extraData
dictionary.
The three-byte key 0x000001
(ICustomValidator.VALIDATOR_KEY
) is reserved to specify the
address of a custom validator, while the three-byte key 0x000002
(ICustomExecutor.EXECUTOR_KEY
)
is reserved to specify the address of a custom executor.
During validate
and execute
respectively, the account should look up if these keys exist, and use the address to
make a call to the validate
or execute
function of the extension, respectively.
Of note, it's possible to specify garbled values for the reserved keys (i.e. not exactly 20 bytes) or addresses that are
not registered extensions. We provide the standard InvalidExtensionValue
and ExtensionNotRegistered
errors so that
validate
and execute
can reject these cases and aid debugging.
Again, see the HappyAccount
for an example implementation.
Implementing Extensions
Extension contracts themselves should implement the ICustomValidator
or ICustomExecutor
interfaces. These
interfaces are straightforward: they must respectively implement a validate(Boop)
or execute(Boop)
function, whose
signature and behaviour should be identical to the corresponding function in the account itself.
An execution extension can make calls on behalf of the account by calling executeCallFromExecutor
on the account.
See the SessionKeyValidator
and BatchCallExecutor
extensions source code for implementation examples.
Using the SessionKeyValidator
-
Install the session key validator on the account via
addExtension
on the account (can be sent as a boop). -
Add a session key by calling
addSessionKey
on the session key validator contract, and passing it the address of the signing key and the authorized target address. This must be sent as a boop, as it registers the key formsg.sender
. -
You can now send boops with
dest
as the authorized target address. Specify the session key validator address as the value forICustomValidator.VALIDATOR_KEY
(0x000001
). The account will use the session key contract to validate the call. -
You can remove a session key by calling
removeSessionKey
on the validator, and you can uninstall the extension viaremoveExtension
on the account.
You can use the Boop client SDK's encodeExtraData
function to prepare the boop's extraData
field.
Note that if you use the Happy Wallet for session keys, you don't need to worry about any of this: just use the
requestSessionKey
function in our SDK, then send transactions to the authorized target address as usual, and the
Happy Wallet will automatically route them to use the registered session key.
Using the BatchCallExecutor
-
Install the session key validator on the account via
addExtension
on the account (can be sent as a boop). -
You can now use the batch call executor by specifying the batch call executor address as the value for
ICustomExecutor.EXECUTOR_KEY
(0x000001
)extraData
key. The calls you want to make should be encoded as a tightly packed array ofCallInfo
structure (address dest
,uint256 value
,bytes callData
) *, and provided as the value for theBatchCallExecutor.BATCH_CALL_INFO_KEY
(0x000100
)extraData
key. Thedest
,value
andcallData
fields of the boop itself are ignored and should be zeroed/empty.* The memory layout for batching calls is: 20 bytes for the first destination, 32 bytes for the first value, 32 bytes for the calldata size of the first calldata, then the first calldata bytes, then repeat for the second batched call and onwards. The total length is encoded in the
extraData
key length.Note: in the future we may change the length of
CallInfo
size to be encoded on 3 bytes instead of 32, since the total length of anextraData
key's value is encoded on 3 bytes anyway (allowing for up to 16MB values, which is more than a node will accept today anyway). -
You can uninstall the extension via
removeExtension
on the account.
You can use the Boop client SDK's encodeExtraData
function to prepare the boop's extraData
field.
We will add a function to pack a CallInfo
array shortly.
Security Considerations
Extensions are extremely powerful. You should make sure you trust them before adding them to your account, as a validator extension can essentially approve any boop on your account, while an executor extension can make any call it wants on your behalf. Please make sure the extensions you use are trusted and review their code if you're able. If you're not sure about an extension and don't want to lose control over your account and assets, don't install it!