Skip to content

Instantly share code, notes, and snippets.

@CJ42
Last active July 29, 2023 15:33
Show Gist options
  • Save CJ42/495485c7b4e79aa7fc20f12e8c59dd04 to your computer and use it in GitHub Desktop.
Save CJ42/495485c7b4e79aa7fc20f12e8c59dd04 to your computer and use it in GitHub Desktop.

LSP6 with LSP7 token

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).

Base: Current Key Manager

beforeLSP7LSP6

Case 1: New LSP6TokenManager

afterLSP7LSP6

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

Case 2: New LSP6TokenManager No ERC1271

image

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 %)

Summary

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)

Assessing gas reduction of marking the _linkToken as immutable.

image

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

Case 3: removing checks for LSP1 + LSP17 Data keys (including internal functions)

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;
    }

image

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 %)

Case 4: going the extra mile and removing the permission name not applicable.

image

Runtime gas cost does not change.

Deployment cost is reduced even more by 170,092 gas (2,386,695 - 2,556,787)

Final Conclusion

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.
@skimaharvey
Copy link

nice one. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment