diff --git a/contracts/contracts/Imports.sol b/contracts/contracts/Imports.sol index 4e7b09d..b8b5b92 100644 --- a/contracts/contracts/Imports.sol +++ b/contracts/contracts/Imports.sol @@ -3,3 +3,4 @@ pragma solidity ^0.8.18; // Import the contract so hardhat compiles it, and we have the ABI available import {MockContract} from "@safe-global/mock-contract/contracts/MockContract.sol"; +import {TestSafeProtocolRegistryUnrestricted} from "@safe-global/safe-core-protocol/contracts/test/TestSafeProtocolRegistryUnrestricted.sol"; diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 85aa20c..1ac7cf0 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -22,6 +22,8 @@ const argv : any = yargs const { NODE_URL, MNEMONIC, INFURA_KEY, ETHERSCAN_API_KEY} = process.env; +import "./src/tasks/test_registry" + const deterministicDeployment = (network: string): DeterministicDeploymentInfo => { const info = getSingletonFactoryInfo(parseInt(network)); if (!info) { diff --git a/contracts/src/tasks/test_registry.ts b/contracts/src/tasks/test_registry.ts new file mode 100644 index 0000000..02764b9 --- /dev/null +++ b/contracts/src/tasks/test_registry.ts @@ -0,0 +1,27 @@ +import "hardhat-deploy"; +import "@nomicfoundation/hardhat-ethers"; +import { task } from "hardhat/config"; +import { getPlugin, getRegistry, getSamplePlugin } from "../utils/contracts"; +import { IntegrationType } from "../utils/constants"; +import { loadPluginMetaData } from "../utils/metadata"; + +task("register-plugin", "Registers the sample Plugin in the Safe{Core} test register") + .setAction(async (_, hre) => { + const registry = await getRegistry(hre) + const plugin = await getSamplePlugin(hre) + await registry.addIntegration(await plugin.getAddress(), IntegrationType.Plugin) + console.log("Registered Plugin registry") + }); + +task("list-plugins", "List available Plugins in the Safe{Core} test register") + .setAction(async (_, hre) => { + const registry = await getRegistry(hre) + const events = await registry.queryFilter(registry.filters.IntegrationAdded) + for (const event of events) { + const plugin = await getPlugin(hre, event.args.integration) + const metaData = await loadPluginMetaData(hre, plugin) + console.log(event.args.integration, metaData) + } + }); + +export { } \ No newline at end of file diff --git a/contracts/test/utils/builder.ts b/contracts/src/utils/builder.ts similarity index 100% rename from contracts/test/utils/builder.ts rename to contracts/src/utils/builder.ts diff --git a/contracts/test/utils/constants.ts b/contracts/src/utils/constants.ts similarity index 100% rename from contracts/test/utils/constants.ts rename to contracts/src/utils/constants.ts diff --git a/contracts/src/utils/contracts.ts b/contracts/src/utils/contracts.ts new file mode 100644 index 0000000..7684ec9 --- /dev/null +++ b/contracts/src/utils/contracts.ts @@ -0,0 +1,18 @@ +import { BaseContract } from "ethers"; +import { BasePlugin, SamplePlugin, TestSafeProtocolRegistryUnrestricted } from "../../typechain-types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { getProtocolRegistryAddress } from "./protocol"; + +export const getInstance = async (hre: HardhatRuntimeEnvironment, name: string, address: string): Promise => { + // TODO: this typecasting should be refactored + return (await hre.ethers.getContractAt(name, address)) as unknown as T; +}; + +export const getSingleton = async (hre: HardhatRuntimeEnvironment, name: string): Promise => { + const deployment = await hre.deployments.get(name); + return getInstance(hre, name, deployment.address); +}; + +export const getPlugin = (hre: HardhatRuntimeEnvironment, address: string) => getInstance(hre, "BasePlugin", address); +export const getSamplePlugin = (hre: HardhatRuntimeEnvironment) => getSingleton(hre, "SamplePlugin"); +export const getRegistry = async (hre: HardhatRuntimeEnvironment) => getInstance(hre, "TestSafeProtocolRegistryUnrestricted", await getProtocolRegistryAddress(hre)); diff --git a/contracts/test/utils/dataTypes.ts b/contracts/src/utils/dataTypes.ts similarity index 100% rename from contracts/test/utils/dataTypes.ts rename to contracts/src/utils/dataTypes.ts diff --git a/contracts/test/utils/metadata.ts b/contracts/src/utils/metadata.ts similarity index 68% rename from contracts/test/utils/metadata.ts rename to contracts/src/utils/metadata.ts index 4ca1522..a4aedb0 100644 --- a/contracts/test/utils/metadata.ts +++ b/contracts/src/utils/metadata.ts @@ -1,6 +1,7 @@ import { AbiCoder, isHexString, keccak256 } from "ethers"; import { BasePlugin, MetaDataProvider } from "../../typechain-types"; -import { getInstance } from "./contracts"; +import { getInstance } from "../utils/contracts"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; interface PluginMetaData { name: string; @@ -17,24 +18,24 @@ const ProviderType_Contract = 2n; const PluginMetaDataType: string[] = ["string name", "string version", "bool requiresRootAccess", "string iconUrl", "string appUrl"]; -const loadPluginMetaDataFromContract = async (provider: string, metaDataHash: string): Promise => { - const providerInstance = await getInstance("MetaDataProvider", provider); +const loadPluginMetaDataFromContract = async (hre: HardhatRuntimeEnvironment, provider: string, metaDataHash: string): Promise => { + const providerInstance = await getInstance(hre, "MetaDataProvider", provider); return await providerInstance.retrieveMetaData(metaDataHash); }; -const loadRawMetaData = async (plugin: BasePlugin, metaDataHash: string): Promise => { +const loadRawMetaData = async (hre: HardhatRuntimeEnvironment, plugin: BasePlugin, metaDataHash: string): Promise => { const [type, source] = await plugin.metaProvider(); switch (type) { case ProviderType_Contract: - return loadPluginMetaDataFromContract(AbiCoder.defaultAbiCoder().decode(["address"], source)[0], metaDataHash); + return loadPluginMetaDataFromContract(hre, AbiCoder.defaultAbiCoder().decode(["address"], source)[0], metaDataHash); default: throw Error("Unsupported MetaDataProviderType"); } }; -export const loadPluginMetaData = async (plugin: BasePlugin): Promise => { +export const loadPluginMetaData = async (hre: HardhatRuntimeEnvironment, plugin: BasePlugin): Promise => { const metaDataHash = await plugin.metaDataHash(); - const metaData = await loadRawMetaData(plugin, metaDataHash); + const metaData = await loadRawMetaData(hre, plugin, metaDataHash); if (metaDataHash !== keccak256(metaData)) throw Error("Invalid meta data retrieved!"); return decodePluginMetaData(metaData); }; diff --git a/contracts/src/utils/protocol.ts b/contracts/src/utils/protocol.ts index db17ace..8eac372 100644 --- a/contracts/src/utils/protocol.ts +++ b/contracts/src/utils/protocol.ts @@ -35,7 +35,8 @@ export const getProtocolRegistryAddress = async(hre: HardhatRuntimeEnvironment): 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 + // We use the unrestricted registry for the demo + const registry = (protocolDeployments as any)[chainId][0].contracts.TestSafeProtocolRegistryUnrestricted.address if (typeof registry !== "string") throw Error("Unexpected Registry") return registry } \ No newline at end of file diff --git a/contracts/test/SamplePlugin.spec.ts b/contracts/test/SamplePlugin.spec.ts index 7f30dcd..6f55ca3 100644 --- a/contracts/test/SamplePlugin.spec.ts +++ b/contracts/test/SamplePlugin.spec.ts @@ -1,8 +1,8 @@ import hre, { deployments } from "hardhat"; import { expect } from "chai"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { getSamplePlugin } from "./utils/contracts"; -import { loadPluginMetaData } from "./utils/metadata"; +import { getSamplePlugin } from "../src/utils/contracts"; +import { loadPluginMetaData } from "../src/utils/metadata"; describe("SamplePlugin", async () => { let user1: SignerWithAddress; @@ -13,7 +13,7 @@ describe("SamplePlugin", async () => { const setup = deployments.createFixture(async ({ deployments }) => { await deployments.fixture(); - const plugin = await getSamplePlugin(); + const plugin = await getSamplePlugin(hre); return { plugin, }; @@ -29,7 +29,7 @@ describe("SamplePlugin", async () => { it("can retrieve meta data for module", async () => { const { plugin } = await setup(); - expect(await loadPluginMetaData(plugin)).to.be.deep.eq({ + expect(await loadPluginMetaData(hre, plugin)).to.be.deep.eq({ name: "Sample Plugin", version: "1.0.0", requiresRootAccess: false, diff --git a/contracts/test/utils/contracts.ts b/contracts/test/utils/contracts.ts deleted file mode 100644 index f27e5fc..0000000 --- a/contracts/test/utils/contracts.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BaseContract } from "ethers"; -import hre, { deployments } from "hardhat"; -import { SamplePlugin } from "../../typechain-types"; - -export const getInstance = async (name: string, address: string): Promise => { - // TODO: this typecasting should be refactored - return (await hre.ethers.getContractAt(name, address)) as unknown as T; -}; - -export const getSingleton = async (name: string): Promise => { - const deployment = await deployments.get(name); - return getInstance(name, deployment.address); -}; - -export const getSamplePlugin = () => getSingleton("SamplePlugin");