The Safe{Core} Protocol is designed to be account agnostic. This initial alpha version focuses on the 1.x versions of Safe Smart Accounts to expedite the development process and gather feedback. These learnings are the foundation upon which the protocol is opened to other account implementations.
The Safe{Core} Protocol Manager contract is the main entity interacting with accounts and vice versa. The Manager expects accounts to implement a specific interface, described in this document.
The Account MUST append the 20 bytes of the msg.sender
address to the calldata to each Account configuration-related method (setting a hook,
adding a plugin, etc., for the full list, refer to Manager's specification). This is required because
to support some functionality, such as function handlers, an Account may be required to execute a CALL
operation that potentially bypasses the authorization to the Manager contract. This is required to allow the Manager to identify the transaction's sender. It is assumed
that the only way to have the msg.sender
equal to the Account address in the call frame is to execute a call through the Account's built-in authorization scheme.
The account MUST implement the following interface:
interface IAccount {
function execTransactionFromModuleReturnData(
address to,
uint256 value,
bytes memory data,
uint8 operation
) external returns (bool success, bytes memory returnData);
}
where:
Type | Name | Description |
---|---|---|
address | to | The address of the contract to be called |
uint256 | value | The amount of native token to be sent. Ignored if operation is DELEGATECALL. |
bytes memory | data | The call data |
uint8 | operation | The operation type to be executed. 0 for CALL, 1 for DELEGATECALL. The DELEGATECALL operation can only be executed by a Plugin with root access permissions. More information about transaction types can be found in the Manager contract specifications. |
The account MUST execute a corresponding operation
to the to
address with the provided value
and data
parameters. The account MUST return a tuple of (bool success, bytes memory returnData)
.
It is RECOMMENDED that the account supports the DELEGATECALL operation.
Hooks do not require any specific interface to be implemented by the account. To use hooks, use the interface defined in the Safe{Core} Protocol Manager specification
The account MUST implement a fallback
function, which forwards all the calldata
to the Safe{Core} Protocol Manager AND appends the 20 bytes of the msg.sender
address to the calldata.
An example implementation of such a fallback
function is:
fallback() external {
bytes32 slot = SAFE_CORE_PROTOCOL_MANAGER_SLOT;
/// @solidity memory-safe-assembly
assembly {
let protocol := sload(slot)
if iszero(protocol) {
return(0, 0)
}
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
mstore(add(ptr, calldatasize()), shl(96, caller()))
// Add 20 bytes for the address appended add the end
let success := call(gas(), protocol, 0, ptr, add(calldatasize(), 20), 0, 0)
returndatacopy(ptr, 0, returndatasize())
if iszero(success) {
revert(ptr, returndatasize())
}
return(ptr, returndatasize())
}
}
- The account MUST follow the requirements defined in the Function Handlers section, or the account MUST forward the EIP-1271 isValidSignature call to the Safe{Core} Validator Manager, following the expected encoding defined in the Signature Validator specs.
- The account MUST implement the following interface:
interface IAccount {
function checkSignatures(bytes32 messageHash, bytes memory signatures) external view;
}
where:
-
checkSignatures
function parameters are:Type Name Description bytes32 messageHash Hash of the message bytes memory signatures Signature bytes
The function MUST revert if the signatures are not valid.
- Safe{Core} Account - starting from version 1.3.0