Here are the stats of the deployment cost and runtime cost of a LSP6 Key Manager (= "LSP6TokenManager
") that is built with only the funtionalities
to manage a LSP7/8 Token (only the path for the setData selectors, no reentrancy checks as it is not applicable to a token and
no isValidSignature(...)
function with check on permission SIGN
, as a token does not have that).
Deployment cost: 3_483_874 - 2_704_450 = 779_424 (-22.37 %)
Calling LSP6.execute(bytes)
to set data on the LSP7 token --> 106,968 - 104,038 = 2,930
Deployment cost reduced even more.
Removing isValidSignature(...)
from the contract decreases deployment cost by 54,715 gas.
3_483_874 - 2_649_735 = 834,139 (-23.94 %)
The bytecode size of the contract reduced as follow (from the current Key Manager to the one without even ERC1271)
- bytecode (= creationCode):
- before = 16,045
- after = 12,292 (- 3,753 bytes)
- deployedBytecode (= runtimeCode):
- before: 15,853
- after: 12,078 (- 3,775 bytes)
runtime cost of LSP6 execute(bytes)
= 104,038 (case 2) - 106,364 = -2,326
Deployment cost of the Key Manager = 2,649,736 (case 2) - 2,647,861 = +1,875
Conclusion: marking the variable as immutable
as a good impact on the runtime cost, but actually increases the deployment cost.l
Since these data keys are not present in a LSP7 + LSP8 token by default, I have removed the following checks:
} else if (
inputDataKey == _LSP1_UNIVERSAL_RECEIVER_DELEGATE_KEY ||
bytes12(inputDataKey) == _LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX
) {
// same as above. If controller has both permissions, do not read the `target` storage
// to save gas by avoiding an extra external `view` call.
if (
controllerPermissions.hasPermission(
_PERMISSION_ADDUNIVERSALRECEIVERDELEGATE |
_PERMISSION_CHANGEUNIVERSALRECEIVERDELEGATE
)
) {
return bytes32(0);
}
return
_getPermissionToSetLSP1Delegate(
controlledContract,
inputDataKey
);
// LSP17Extension:<bytes4>
} else if (bytes12(inputDataKey) == _LSP17_EXTENSION_PREFIX) {
// same as above. If controller has both permissions, do not read the `target` storage
// to save gas by avoiding an extra external `view` call.
if (
controllerPermissions.hasPermission(
_PERMISSION_ADDEXTENSIONS | _PERMISSION_CHANGEEXTENSIONS
)
) {
return bytes32(0);
}
return
_getPermissionToSetLSP17Extension(
controlledContract,
inputDataKey
);
and removed these internal functions:
/**
* @dev retrieve the permission required to either add or change the address
* of a LSP1 Universal Receiver Delegate stored under a specific LSP1 data key.
* @param controlledContract the address of the ERC725Y contract where the data key is verified.
* @param lsp1DelegateDataKey either the data key for the default `LSP1UniversalReceiverDelegate`,
* or a data key for a specific `LSP1UniversalReceiverDelegate:<typeId>`, starting with `_LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX`.
* @return either ADD or CHANGE UNIVERSALRECEIVERDELEGATE.
*/
function _getPermissionToSetLSP1Delegate(
address controlledContract,
bytes32 lsp1DelegateDataKey
) internal view virtual returns (bytes32) {
return
ERC725Y(controlledContract).getData(lsp1DelegateDataKey).length == 0
? _PERMISSION_ADDUNIVERSALRECEIVERDELEGATE
: _PERMISSION_CHANGEUNIVERSALRECEIVERDELEGATE;
}
/**
* @dev Verify if `controller` has the required permissions to either add or change the address
* of an LSP0 Extension stored under a specific LSP17Extension data key
* @param controlledContract the address of the ERC725Y contract where the data key is verified.
* @param lsp17ExtensionDataKey the dataKey to set with `_LSP17_EXTENSION_PREFIX` as prefix.
*/
function _getPermissionToSetLSP17Extension(
address controlledContract,
bytes32 lsp17ExtensionDataKey
) internal view virtual returns (bytes32) {
return
ERC725Y(controlledContract).getData(lsp17ExtensionDataKey).length ==
0
? _PERMISSION_ADDEXTENSIONS
: _PERMISSION_CHANGEEXTENSIONS;
}
runtime cost of execute(bytes)
= -61 gas
deployment cost = 2,649,736 - 2,556,787 = 92,949! --> this mean that these 2 data keys that will never be checked at runtime with this setup have increased the deployment cost of the LSP6TokenManager
by almost 100k+ gas.
Compared to base case (the initial Key Manager)
deployment cost = 2,556,787 - 3,483,863 = 927,076 (-26.61 %) runtime cost of setting data = 106,968 - 103,977 = -2,991 (-2.87 %)
Runtime gas cost does not change.
Deployment cost is reduced even more by 170,092 gas (2,386,695 - 2,556,787)
Compared to initial Key Manager the deployment cost for a LSP6TokenManager
vs the deployment cost for a LSP6KeyManager
is:
2,386,695 - 3,483,863 = 1,097,168 (-31.49 %)
This mean that when the LSP6KeyManager
is used to control a LSP7/8 Token:
- 1/3rd of the bytecode in unecessary
- A simpler Key Manager deployed (a
LSP6TokenManager
) would reduce the deployment cost by 1.1 Million gas (-31.5 % savings) - We could save around 3k gas on
setData(...)
in the Token contract via the Key Manager (around 2.9 % saving), which is not much.
nice one. Thanks