From 4e29e09b572ecf3e63f48fcc09e848f3a3ecf621 Mon Sep 17 00:00:00 2001 From: Richard Meissner Date: Thu, 13 Jul 2023 21:52:32 +0200 Subject: [PATCH] Use safe-core-protocol package (#12) * Use safe-core-protocol package * Update protocol dependency * Update Sample Plugin --- contracts/.gitignore | 1 + contracts/contracts/DataTypes.sol | 20 --- contracts/contracts/Plugins.sol | 24 ++-- contracts/contracts/interfaces/Accounts.sol | 21 ---- .../contracts/interfaces/Integrations.sol | 118 ------------------ contracts/contracts/interfaces/Manager.sol | 32 ----- contracts/contracts/interfaces/Registry.sol | 10 -- contracts/hardhat.config.ts | 8 +- contracts/package.json | 3 +- contracts/{ => src}/deploy/deploy_plugin.ts | 6 +- contracts/src/utils/protocol.ts | 41 ++++++ contracts/yarn.lock | 15 ++- 12 files changed, 78 insertions(+), 221 deletions(-) delete mode 100644 contracts/contracts/DataTypes.sol delete mode 100644 contracts/contracts/interfaces/Accounts.sol delete mode 100644 contracts/contracts/interfaces/Integrations.sol delete mode 100644 contracts/contracts/interfaces/Manager.sol delete mode 100644 contracts/contracts/interfaces/Registry.sol rename contracts/{ => src}/deploy/deploy_plugin.ts (74%) create mode 100644 contracts/src/utils/protocol.ts diff --git a/contracts/.gitignore b/contracts/.gitignore index ff0f8bc..6608dbb 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -1,4 +1,5 @@ node_modules +build .env coverage coverage.json diff --git a/contracts/contracts/DataTypes.sol b/contracts/contracts/DataTypes.sol deleted file mode 100644 index 32b13b3..0000000 --- a/contracts/contracts/DataTypes.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.18; - -struct SafeProtocolAction { - address payable to; - uint256 value; - bytes data; -} - -struct SafeTransaction { - SafeProtocolAction[] actions; - uint256 nonce; - bytes32 metaHash; -} - -struct SafeRootAccess { - SafeProtocolAction action; - uint256 nonce; - bytes32 metaHash; -} diff --git a/contracts/contracts/Plugins.sol b/contracts/contracts/Plugins.sol index 497f88f..66442a3 100644 --- a/contracts/contracts/Plugins.sol +++ b/contracts/contracts/Plugins.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8.18; -import {ISafe} from "./interfaces/Accounts.sol"; -import {ISafeProtocolPlugin} from "./interfaces/Integrations.sol"; -import {ISafeProtocolManager} from "./interfaces/Manager.sol"; -import {SafeTransaction, SafeRootAccess} from "./DataTypes.sol"; +import {ISafe} from "@safe-global/safe-core-protocol/contracts/interfaces/Accounts.sol"; +import {ISafeProtocolPlugin} from "@safe-global/safe-core-protocol/contracts/interfaces/Integrations.sol"; +import {ISafeProtocolManager} from "@safe-global/safe-core-protocol/contracts/interfaces/Manager.sol"; +import {SafeTransaction, SafeRootAccess} from "@safe-global/safe-core-protocol/contracts/DataTypes.sol"; enum MetaDataProviderType { IPFS, @@ -75,15 +75,15 @@ abstract contract BasePlugin is ISafeProtocolPlugin, MetaDataProvider { } contract SamplePlugin is BasePlugin { - constructor() - BasePlugin(PluginMetaData({name: "Sample Plugin", version: "1.0.0", requiresRootAccess: false, iconUrl: "", appUrl: ""})) - {} + ISafeProtocolManager public immutable manager; - function executeFromPlugin( - ISafeProtocolManager manager, - ISafe safe, - SafeTransaction calldata safetx - ) external returns (bytes[] memory data) { + constructor( + ISafeProtocolManager _manager + ) BasePlugin(PluginMetaData({name: "Sample Plugin", version: "1.0.0", requiresRootAccess: false, iconUrl: "", appUrl: ""})) { + manager = _manager; + } + + function executeFromPlugin(ISafe safe, SafeTransaction calldata safetx) external returns (bytes[] memory data) { (data) = manager.executeTransaction(safe, safetx); } } diff --git a/contracts/contracts/interfaces/Accounts.sol b/contracts/contracts/interfaces/Accounts.sol deleted file mode 100644 index b8d8767..0000000 --- a/contracts/contracts/interfaces/Accounts.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.18; - -/** - * @title ISafe Declares the functions that are called on a Safe by Safe protocol. - */ -interface ISafe { - function execTransactionFromModule( - address payable to, - uint256 value, - bytes calldata data, - uint8 operation - ) external returns (bool success); - - function execTransactionFromModuleReturnData( - address to, - uint256 value, - bytes memory data, - uint8 operation - ) external returns (bool success, bytes memory returnData); -} diff --git a/contracts/contracts/interfaces/Integrations.sol b/contracts/contracts/interfaces/Integrations.sol deleted file mode 100644 index 3c692a5..0000000 --- a/contracts/contracts/interfaces/Integrations.sol +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.18; -import {ISafe} from "./Accounts.sol"; -import {SafeTransaction, SafeRootAccess} from "../DataTypes.sol"; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -/** - * @title ISafeProtocolStaticFunctionHandler - An interface that a Safe functionhandler should implement - * @notice In Safe protocol, a function handler can be used to add additional functionality to a Safe. - * TODO: Add more explaination. - */ -interface ISafeProtocolFunctionHandler { - /** - * @notice TODO: Add more explaination - * @param safe A Safe instance - * @param sender Address of the sender - * @param value Amount of ETH - * @param data Arbitrary length bytes - * @return result Arbitrary length bytes containing result of the operation - */ - function handle(ISafe safe, address sender, uint256 value, bytes calldata data) external returns (bytes memory result); -} - -/** - * @title ISafeProtocolStaticFunctionHandler - An interface that a Safe functionhandler should implement in case when handling static calls - * @notice In Safe protocol, a function handler can be used to add additional functionality to a Safe. - * TODO: Add more explaination. - */ -interface ISafeProtocolStaticFunctionHandler { - /** - * @notice TODO: Add more explaination - * @param safe A Safe instance - * @param sender Address of the sender - * @param value Amount of ETH - * @param data Arbitrary length bytes - * @return result Arbitrary length bytes containing result of the operation - */ - function handle(ISafe safe, address sender, uint256 value, bytes calldata data) external view returns (bytes memory result); -} - -/** - * @title ISafeProtocolHooks - An interface that a Safe hook should implement. - * @notice In Safe protocol, a hook can approve or deny transactions based on the logic it implements. - */ -interface ISafeProtocolHooks is IERC165 { - /** - * @notice A function that will be called by a safe before the execution of a transaction if the hook is enabled - * @dev Add custom logic in this function to validate the pre-state and contents of transaction for non-root access. - * @param safe A Safe instance - * @param tx A struct of type SafeTransaction that contains the details of the transaction. - * @param executionType uint256 - * @param executionMeta Arbitrary length of bytes - * @return preCheckData bytes - */ - function preCheck( - ISafe safe, - SafeTransaction calldata tx, - uint256 executionType, - bytes calldata executionMeta - ) external returns (bytes memory preCheckData); - - /** - * @notice A function that will be called by a safe before the execution of a transaction if the hook is enabled and - * transaction requies tool access. - * @dev Add custom logic in this function to validate the pre-state and contents of transaction for root access. - * @param safe A Safe instance - * @param rootAccess DataTypes.SafeRootAccess - * @param executionType uint256 - * @param executionMeta bytes - * @return preCheckData bytes - */ - function preCheckRootAccess( - ISafe safe, - SafeRootAccess calldata rootAccess, - uint256 executionType, - bytes calldata executionMeta - ) external returns (bytes memory preCheckData); - - /** - * @notice A function that will be called by a safe after the execution of a transaction if the hook is enabled. A hook should revert if the post state of after the transaction is not as expected. - * @dev Add custom logic in this function to validate the post-state after the transaction is executed. - * @param safe ISafe - * @param success bool - * @param preCheckData Arbitrary length bytes that was returned by during pre-check of the transaction. - */ - function postCheck(ISafe safe, bool success, bytes calldata preCheckData) external; -} - -/** - * @title ISafeProtocolPlugin - An interface that a Safe plugin should implement - */ -interface ISafeProtocolPlugin { - /** - * @notice A funtion that returns name of the plugin - * @return name string name of the plugin - */ - function name() external view returns (string memory name); - - /** - * @notice A funtion that returns version of the plugin - * @return version string version of the plugin - */ - function version() external view returns (string memory version); - - /** - * @notice A funtion that returns version of the plugin. - * TODO: Define types of meta provider and possible values of location in each of the cases. - * @return providerType uint256 Type of meta provider - * @return location bytes - */ - function metaProvider() external view returns (uint256 providerType, bytes memory location); - - /** - * @notice A function that indicates if the plugin requires root access to a Safe. - * @return requiresRootAccess True if root access is required, false otherwise. - */ - function requiresRootAccess() external view returns (bool requiresRootAccess); -} diff --git a/contracts/contracts/interfaces/Manager.sol b/contracts/contracts/interfaces/Manager.sol deleted file mode 100644 index b23f223..0000000 --- a/contracts/contracts/interfaces/Manager.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.18; -import {ISafe} from "./Accounts.sol"; -import {SafeRootAccess, SafeTransaction} from "../DataTypes.sol"; - -/** - * @title ISafeProtocolManager interface a Manager should implement - * @notice A mediator checks the status of the integration through the registry and allows only - * listed and non-flagged integrations to execute transactions. A Safe account should - * add a mediator as a plugin. - */ -interface ISafeProtocolManager { - /** - * @notice This function allows enabled plugins to execute non-delegate call transactions thorugh a Safe. - * It should validate the status of the plugin through the registry and allows only listed and non-flagged integrations to execute transactions. - * @param safe Address of a Safe account - * @param transaction SafeTransaction instance containing payload information about the transaction - * @return data Array of bytes types returned upon the successful execution of all the actions. The size of the array will be the same as the size of the actions - * in case of succcessful execution. Empty if the call failed. - */ - function executeTransaction(ISafe safe, SafeTransaction calldata transaction) external returns (bytes[] memory data); - - /** - * @notice This function allows enabled plugins to execute delegate call transactions thorugh a Safe. - * It should validate the status of the plugin through the registry and allows only listed and non-flagged integrations to execute transactions. - * @param safe Address of a Safe account - * @param rootAccess SafeTransaction instance containing payload information about the transaction - * @return data Arbitrary length bytes data returned upon the successful execution. The size of the array will be the same as the size of the actions - * in case of succcessful execution. Empty if the call failed. - */ - function executeRootAccess(ISafe safe, SafeRootAccess calldata rootAccess) external returns (bytes memory data); -} diff --git a/contracts/contracts/interfaces/Registry.sol b/contracts/contracts/interfaces/Registry.sol deleted file mode 100644 index d078c65..0000000 --- a/contracts/contracts/interfaces/Registry.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.18; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -interface ISafeProtocolRegistry is IERC165 { - /// @param integration Address of the integration that should be checked - /// @return listedAt MUST return the block number when the integration was listed in the registry (or 0 if not listed) - /// @return flaggedAt MUST return the block number when the integration was listed in the flagged as faulty (or 0 if not flagged) - function check(address integration) external view returns (uint64 listedAt, uint64 flaggedAt); -} diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 503f48c..85aa20c 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -6,7 +6,7 @@ import yargs from "yargs"; import { HttpNetworkUserConfig } from "hardhat/types"; import "hardhat-deploy"; import { DeterministicDeploymentInfo } from "hardhat-deploy/dist/types"; -import { getSingletonFactoryInfo } from "@gnosis.pm/safe-singleton-factory"; +import { getSingletonFactoryInfo } from "@safe-global/safe-singleton-factory"; import { ethers } from "ethers"; // Load environment variables. @@ -54,6 +54,12 @@ const config: HardhatUserConfig = { gasReporter: { enabled: (process.env.REPORT_GAS) ? true : false }, + paths: { + artifacts: "build/artifacts", + cache: "build/cache", + deploy: "src/deploy", + sources: "contracts", + }, networks: { hardhat: { allowUnlimitedContractSize: true, diff --git a/contracts/package.json b/contracts/package.json index fdd626c..d6c25cf 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -22,7 +22,8 @@ "@nomicfoundation/hardhat-verify": "^1.0.0", "@openzeppelin/contracts": "^4.9.1", "@safe-global/mock-contract": "^4.0.0", - "@gnosis.pm/safe-singleton-factory": "^1.0.14", + "@safe-global/safe-singleton-factory": "^1.0.14", + "@safe-global/safe-core-protocol": "^0.1.0-alpha.3", "@typechain/ethers-v6": "^0.4.0", "@typechain/hardhat": "^8.0.0", "@types/chai": "^4.2.0", diff --git a/contracts/deploy/deploy_plugin.ts b/contracts/src/deploy/deploy_plugin.ts similarity index 74% rename from contracts/deploy/deploy_plugin.ts rename to contracts/src/deploy/deploy_plugin.ts index 7613673..7c3b288 100644 --- a/contracts/deploy/deploy_plugin.ts +++ b/contracts/src/deploy/deploy_plugin.ts @@ -1,14 +1,18 @@ import { DeployFunction } from "hardhat-deploy/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { getProtocolManagerAddress } from "../utils/protocol"; const deploy: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, getNamedAccounts } = hre; const { deployer } = await getNamedAccounts(); const { deploy } = deployments; + const manager = await getProtocolManagerAddress(hre) + console.log({manager}) + await deploy("SamplePlugin", { from: deployer, - args: [], + args: [manager], log: true, deterministicDeployment: true, }); diff --git a/contracts/src/utils/protocol.ts b/contracts/src/utils/protocol.ts new file mode 100644 index 0000000..db17ace --- /dev/null +++ b/contracts/src/utils/protocol.ts @@ -0,0 +1,41 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import protocolDeployments from "@safe-global/safe-core-protocol" +import { id } from "ethers"; + +const deployMock = async(hre: HardhatRuntimeEnvironment, name: string): Promise => { + const { deployments, getNamedAccounts } = hre; + const { deployer } = await getNamedAccounts(); + const { deploy } = deployments; + + const result = await deploy("MockContract", { + from: deployer, + args: [], + log: true, + deterministicDeployment: id(name), + }); + return result.address +} + +export const getProtocolManagerAddress = async(hre: HardhatRuntimeEnvironment): Promise => { + const chainId = await hre.getChainId() + + // For the tests we deploy a mock for the manager + if (chainId === "31337") return deployMock(hre, "ManagerMock") + + if (!(chainId in protocolDeployments)) throw Error("Unsupported Chain") + const manager = (protocolDeployments as any)[chainId][0].contracts.SafeProtocolManager.address + if (typeof manager !== "string") throw Error("Unexpected Manager") + return manager +} + +export const getProtocolRegistryAddress = async(hre: HardhatRuntimeEnvironment): Promise => { + const chainId = await hre.getChainId() + + // For the tests we deploy a mock for the registry + if (chainId === "31337") return deployMock(hre, "RegistryMock") + + if (!(chainId in protocolDeployments)) throw Error("Unsupported Chain") + const registry = (protocolDeployments as any)[chainId][0].contracts.SafeProtocolRegistry.address + if (typeof registry !== "string") throw Error("Unexpected Registry") + return registry +} \ No newline at end of file diff --git a/contracts/yarn.lock b/contracts/yarn.lock index 1020edd..5cfc65a 100644 --- a/contracts/yarn.lock +++ b/contracts/yarn.lock @@ -450,11 +450,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@gnosis.pm/safe-singleton-factory@^1.0.14": - version "1.0.14" - resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-singleton-factory/-/safe-singleton-factory-1.0.14.tgz#42dae9a91fda21b605f94bfe310a7fccc6a4d738" - integrity sha512-xZ26c9uKzpd5Sm8ux0sZHt5QC8n+Q2z1/X5xjPnd8aT5EcKH5t1GgLbAqjrMFmXVIOkiWSc7wi2Bj4XfgtiyaQ== - "@humanwhocodes/config-array@^0.11.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -793,6 +788,16 @@ resolved "https://registry.yarnpkg.com/@safe-global/mock-contract/-/mock-contract-4.0.0.tgz#8e1e17e93af5d4b343a6bb6cef8c1f513cb7a92e" integrity sha512-6ijStTgQI6JzYe8Nsc4j1VW4XQ89qCl7ZkRGxwwlxnaOMDYzVekwPACbg2kDDzhtJ4p8vSvE6ZroxSkvP7610A== +"@safe-global/safe-core-protocol@^0.1.0-alpha.3": + version "0.1.0-alpha.3" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-protocol/-/safe-core-protocol-0.1.0-alpha.3.tgz#355b81283b4ce611aa651af76186b1cd91ea0f45" + integrity sha512-iXIaqOrPuJVkTyLHlnzxa5+/oicK43snL6iH/m1f7nFqySvUlXcV29MeS9b3S5a6k8r3cMX9IlmK6GpWk0tx4w== + +"@safe-global/safe-singleton-factory@^1.0.14": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@safe-global/safe-singleton-factory/-/safe-singleton-factory-1.0.14.tgz#a4c3937670946d21428b45f09c779d672cc716de" + integrity sha512-5dyEfPU8oVY/2whxcc14eqWS2WJ8V1j1h3XiretSi9bb09IA+/FTlpooArtOiNs14v6KsMMRnwsHhpDQuZV6Sw== + "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938"