diff --git a/docs/setup_guide.md b/docs/setup_guide.md index 127a9ae..8256ffe 100644 --- a/docs/setup_guide.md +++ b/docs/setup_guide.md @@ -10,18 +10,15 @@ To start the process you need to create a Safe on the Rinkeby test network (e.g. For the hardhat tasks to work the environment needs to be properly configured. See the [sample env file](../.env.sample) for more information. -The guide will use the Rinkeby ETH Realitio contract at [`0x3D00D77ee771405628a4bA4913175EcC095538da`](https://rinkeby.etherscan.io/address/0x3D00D77ee771405628a4bA4913175EcC095538da#code). Other network addresses can be found in the truffle build folder on the [Realitio GitHub repo](https://github.com/realitio/realitio-contracts). E.g. on mainnet the ETH Realitio contract can be found at [`0x325a2e0f3cca2ddbaebb4dfc38df8d19ca165b47`](https://etherscan.io/address/0x325a2e0f3cca2ddbaebb4dfc38df8d19ca165b47#code). +The guide will use the Rinkeby ETH RealitioV3 contract at [`0xDf33060F476F8cff7511F806C72719394da1Ad64`](https://rinkeby.etherscan.io/address/0xDf33060F476F8cff7511F806C72719394da1Ad64#code). Other network addresses can be found in the truffle build folder on the [Realitio GitHub repo](https://github.com/RealityETH/monorepo/tree/main/packages/contracts/chains/deployments). DISCLAIMER: Check the deployed Realitio contracts before using them. -## Setting up the module - -The first step is to deploy the module. Every DAO will have their own module. The module is linked to a DAO (called avatar in the contract) and an oracle (e.g. Realitio). These cannot be changed after deployment. - -As part of the setup you need to define or choose a template on Realitio. More information can be found in [their docs](https://github.com/realitio/realitio-dapp#structuring-and-fetching-information) ### Setup the Realitio template +As part of the setup you need to define or choose a template on Realitio. More information can be found in [their docs](https://github.com/realitio/realitio-dapp#structuring-and-fetching-information). + To define your own template a hardhat task is provided in the repository. It is possible to provide a template to that task via `--template` else the [default template](../src/tasks/defaultTemplate.json) is used. The template should have the following format: @@ -46,40 +43,46 @@ The template should have the following format: Using this template you can run the task by using `yarn hardhat --network createDaoTemplate --oracle --template ` and this should provide you with a template id. An example for this on Rinkeby would be (using the default template): -`yarn hardhat --network rinkeby createDaoTemplate ---oracle 0x3D00D77ee771405628a4bA4913175EcC095538da` +`yarn hardhat --network rinkeby createDaoTemplate ---oracle 0xDf33060F476F8cff7511F806C72719394da1Ad64` For this guide we will assume that the returned template id is `0x0000000000000000000000000000000000000000000000000000000000000dad` +You can also create your template from this (UI)[https://reality.eth.link/app/template-generator/] + ### Deploying the module -Hardhat tasks can be used to deploy a reality module instance. There are two different tasks, the first one is through a normal deployment and passing arguments to the constructor (with the task `setup`), or, deploy the Module through a [Minimal Proxy Factory](https://eips.ethereum.org/EIPS/eip-1167) and save on gas costs (with the task `factorySetup`) - In rinkeby the address of the Proxy Factory is: `0xd067410a85ffC8C55f7245DE4BfE16C95329D232` and the master copy of the Reality Module: `0x4D0D4Bd6eCA52f2F931c099B6a8a8B2ae85FFD4E`. +The module has nine attributes which are: +- Owner: address that can call setter functions +- Avatar: address of the DAO (e.g Safe) +- Target: address that the module will call `execModuleTransaction()` on. +- Oracle: address of the oracle (e.g RealitioV3) +- Timeout: Timeout in seconds that should be required for the oracle +- Cooldown: Amount in seconds of cooldown required before the transaction can be executed +- Expiration: Duration that a transaction is valid in seconds (or 0 if valid forever) after the cooldown +- Bond: Minimum bond that is required for an answer to be accepted +- Template ID: ID of the template that should be used for proposal questions (see https://github.com/realitio/realitio-dapp#structuring-and-fetching-information) -Now that we have a template, a hardhat task can be used to deploy a Reality module instance. These tasks requires the following parameters: -- `avatar` - the address of the avatar. +Hardhat tasks can be used to deploy a Reality Module instance. There are two different ways to deploy the module, the first one is through a normal deployment and passing arguments to the constructor (without the `proxied` flag), or, deploy the module through a [Minimal Proxy Factory](https://eips.ethereum.org/EIPS/eip-1167) and save on gas costs (with the `proxied` flag) - The master copy and factory address can be found in the [zodiac repository](https://github.com/gnosis/zodiac/blob/master/src/factory/constants.ts) and these are the addresses that are going to be used when deploying the module through factory. + +This task requires the following parameters: - `owner` - the address of the owner -- `oracle` - the address of the Realitio contract -- `template` - the template to be used with Realitio +- `avatar` - the address of the avatar. +- `target` - the address of the target. +- `oracle` - the address of the RealitioV3 contract +- `template` - the template to be used with RealitioV3 +- `iserc20` (optional) - If set to true, the module `RealityERC20` is going to be deployed, otherwise `RealityETH` is deployed. By default is false +- `proxied` (optional) - Deploys the module through a proxy factory -There are also optional parameters, for more information run `yarn hardhat setup --help` or `yarn hardhat factory-setup --help`. +There are more optional parameters, for more information run `yarn hardhat setup --help`. An example for this on Rinkeby would be: -`yarn hardhat --network rinkeby setup --owner --avatar --oracle 0x3D00D77ee771405628a4bA4913175EcC095538da --template 0x0000000000000000000000000000000000000000000000000000000000000dad` - -or - -`yarn hardhat --network rinkeby factorySetup --factory --mastercopy --owner --avatar --oracle 0x3D00D77ee771405628a4bA4913175EcC095538da --template 0x0000000000000000000000000000000000000000000000000000000000000dad` - -or - -`yarn hardhat --network rinkeby factory-setup --factory --mastercopy --dao --oracle 0x3D00D77ee771405628a4bA4913175EcC095538da --template 0x0000000000000000000000000000000000000000000000000000000000000dad` - -This should return the address of the deployed Reality module. For this guide we assume this to be `0x4242424242424242424242424242424242424242` +`yarn hardhat --network rinkeby setup --owner --avatar --target --oracle 0xDf33060F476F8cff7511F806C72719394da1Ad64 --template 0x0000000000000000000000000000000000000000000000000000000000000dad` -Once the module is deployed you should verify the source code (Note: If you used the factory deployment the contract should be already verified). If you use a network that is Etherscan compatible and you configure the `ETHERSCAN_API_KEY` in your environment you can use the provided hardhat task to do this. +Once the module is deployed you should verify the source code (Note: Probably etherscan will verify it automatically, but just in case). If you use a network that is Etherscan compatible and you configure the `ETHERSCAN_API_KEY` in your environment you can use the provided hardhat task to do this. An example for this on Rinkeby would be: -`yarn hardhat --network rinkeby verifyEtherscan --module 0x4242424242424242424242424242424242424242 --owner --avatar --oracle 0x3D00D77ee771405628a4bA4913175EcC095538da --template 0x0000000000000000000000000000000000000000000000000000000000000dad` +`yarn hardhat --network rinkeby verifyEtherscan --module 0x4242424242424242424242424242424242424242 --owner --avatar --target --oracle 0xDf33060F476F8cff7511F806C72719394da1Ad64 --template 0x0000000000000000000000000000000000000000000000000000000000000dad` ### Enabling the module diff --git a/package.json b/package.json index 5131e63..67f5aa2 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "author": "richard@gnosis.io", "license": "MIT", "devDependencies": { - "@gnosis.pm/zodiac": "0.0.1-prealpha2", + "@gnosis.pm/zodiac": "0.0.1-prealpha7", "@nomiclabs/hardhat-ethers": "^2.0.0", "@nomiclabs/hardhat-etherscan": "^2.1.0", "@nomiclabs/hardhat-waffle": "^2.0.0", diff --git a/src/tasks/setup.ts b/src/tasks/setup.ts index 966ee1e..9cf0944 100644 --- a/src/tasks/setup.ts +++ b/src/tasks/setup.ts @@ -1,41 +1,78 @@ import "hardhat-deploy"; import "@nomiclabs/hardhat-ethers"; import { task, types } from "hardhat/config"; +import { deployAndSetUpModule } from "@gnosis.pm/zodiac" import defaultTemplate from "./defaultTemplate.json"; -import { Contract } from "ethers"; -import { AbiCoder } from "ethers/lib/utils"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; -const FIRST_ADDRESS = "0x0000000000000000000000000000000000000001"; -const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; +interface RealityTaskArgs { + owner: string + avatar: string + target: string + oracle: string + timeout: string + cooldown: string + expiration: string + bond: string + template: string + proxied: boolean + iserc20: boolean +} -task("setup", "Provides the clearing price to an auction") - .addParam("owner", "Address of the owner", undefined, types.string) - .addParam("avatar", "Address of the avatar (e.g. Safe)", undefined, types.string) - .addParam("oracle", "Address of the oracle (e.g. Realitio)", undefined, types.string) - .addParam( - "template", - "Template that should be used for proposal questions (See https://github.com/realitio/realitio-dapp#structuring-and-fetching-information)", - undefined, - types.string - ) - .addParam("timeout", "Timeout in seconds that should be required for the oracle", 48 * 3600, types.int, true) - .addParam("cooldown", "Cooldown in seconds that should be required after a oracle provided answer", 24 * 3600, types.int, true) - .addParam("expiration", "Time duration in seconds for which a positive answer is valid. After this time the answer is expired", 7 * 24 * 3600, types.int, true) - .addParam("bond", "Minimum bond that is required for an answer to be accepted", "0", types.string, true) - .setAction(async (taskArgs, hardhatRuntime) => { - const [caller] = await hardhatRuntime.ethers.getSigners(); - console.log("Using the account:", caller.address); - const Module = await hardhatRuntime.ethers.getContractFactory("RealityModule"); - const module = await Module.deploy(taskArgs.owner, taskArgs.avatar, taskArgs.oracle, taskArgs.timeout, taskArgs.cooldown, taskArgs.expiration, taskArgs.bond, taskArgs.template); +const deployRealityModule = async (taskArgs: RealityTaskArgs, hardhatRuntime: HardhatRuntimeEnvironment) => { + const [caller] = await hardhatRuntime.ethers.getSigners(); + console.log("Using the account:", caller.address); + + if (taskArgs.proxied) { + const chainId = await hardhatRuntime.getChainId(); + const module = taskArgs.iserc20 ? "realityERC20" : "realityETH" + const { transaction } = deployAndSetUpModule( + module, + { + types: [ + "address", + "address", + "address", + "address", + "uint32", + "uint32", + "uint32", + "uint256", + "uint256", + ], + values: [ + taskArgs.owner, + taskArgs.avatar, + taskArgs.target, + taskArgs.oracle, + taskArgs.timeout, + taskArgs.cooldown, + taskArgs.expiration, + taskArgs.bond, + taskArgs.template, + ], + }, + hardhatRuntime.ethers.provider, + Number(chainId), + Date.now().toString() + ); + const deploymentTransaction = await caller.sendTransaction(transaction); + const receipt = await deploymentTransaction.wait(); + console.log("Module deployed to:", receipt.logs[1].address) + return + } - console.log("Module deployed to:", module.address); - }); + const ModuleName = taskArgs.iserc20 ? "RealityModuleERC20" : "RealityModuleETH" + const Module = await hardhatRuntime.ethers.getContractFactory(ModuleName); + const module = await Module.deploy(taskArgs.owner, taskArgs.avatar, taskArgs.target, taskArgs.oracle, taskArgs.timeout, taskArgs.cooldown, taskArgs.expiration, taskArgs.bond, taskArgs.template); + await module.deployTransaction.wait() + console.log("Module deployed to:", module.address); +} -task("factorySetup", "Deploy and initialize Reality Module through a Proxy Factory") - .addParam("factory", "Address of the Proxy Factory", undefined, types.string) - .addParam("mastercopy", "Address of the Reality Module Master Copy", undefined, types.string) +task("setup", "Provides the clearing price to an auction") .addParam("owner", "Address of the owner", undefined, types.string) .addParam("avatar", "Address of the avatar (e.g. Safe)", undefined, types.string) + .addParam("target", "Address of the target", undefined, types.string) .addParam("oracle", "Address of the oracle (e.g. Realitio)", undefined, types.string) .addParam( "template", @@ -47,43 +84,15 @@ task("factorySetup", "Deploy and initialize Reality Module through a Proxy Facto .addParam("cooldown", "Cooldown in seconds that should be required after a oracle provided answer", 24 * 3600, types.int, true) .addParam("expiration", "Time duration in seconds for which a positive answer is valid. After this time the answer is expired", 7 * 24 * 3600, types.int, true) .addParam("bond", "Minimum bond that is required for an answer to be accepted", "0", types.string, true) - .setAction(async (taskArgs, hardhatRuntime) => { - const [caller] = await hardhatRuntime.ethers.getSigners(); - console.log("Using the account:", caller.address); + .addParam("proxied", "Deploys module through proxy factory", false, types.boolean, true) + .addParam("iserc20", "Defines if Reality is deployed for ETH or ERC20. By default is false", false, types.boolean, true) + .setAction(deployRealityModule); - const FactoryAbi = [ - `function deployModule( - address masterCopy, - bytes memory initializer - ) public returns (address proxy)`, - ]; - - const Factory = new Contract(taskArgs.factory, FactoryAbi, caller) - const Module = await hardhatRuntime.ethers.getContractFactory("RealityModule"); - - const encodedParams = new AbiCoder().encode( - ["address", "address", "address", "uint32", "uint32", "uint32", "uint256", "uint256"], - [ - taskArgs.owner, - taskArgs.avatar, - taskArgs.oracle, - taskArgs.timeout, - taskArgs.cooldown, - taskArgs.expiration, - taskArgs.bond, - taskArgs.template - ] - ) - - const initParams = Module.interface.encodeFunctionData('setUp', [encodedParams]) - - const receipt = await Factory.deployModule(taskArgs.mastercopy, initParams).then((tx: any) => tx.wait(3)); - console.log("Module deployed to:", receipt.logs[1].address); - }); task("verifyEtherscan", "Verifies the contract on etherscan") .addParam("module", "Address of the module", undefined, types.string) .addParam("owner", "Address of the owner", undefined, types.string) .addParam("avatar", "Address of the avatar (e.g. Safe)", undefined, types.string) + .addParam("target", "Address of the target", undefined, types.string) .addParam("oracle", "Address of the oracle (e.g. Realitio)", undefined, types.string) .addParam( "template", @@ -95,17 +104,17 @@ task("verifyEtherscan", "Verifies the contract on etherscan") .addParam("cooldown", "Cooldown in seconds that should be required after a oracle provided answer", 24 * 3600, types.int, true) .addParam("expiration", "Time duration in seconds for which a positive answer is valid. After this time the answer is expired", 7 * 24 * 3600, types.int, true) .addParam("bond", "Minimum bond that is required for an answer to be accepted", "0", types.string, true) - .setAction(async (taskArgs, hardhatRuntime) => { + .setAction(async (taskArgs: RealityTaskArgs & { module: string }, hardhatRuntime) => { await hardhatRuntime.run("verify", { address: taskArgs.module, constructorArgsParams: [ - taskArgs.owner, taskArgs.avatar, taskArgs.oracle, `${taskArgs.timeout}`, `${taskArgs.cooldown}`, `${taskArgs.expiration}`, `${taskArgs.bond}`, taskArgs.template + taskArgs.owner, taskArgs.avatar, taskArgs.oracle, taskArgs.target, `${taskArgs.timeout}`, `${taskArgs.cooldown}`, `${taskArgs.expiration}`, `${taskArgs.bond}`, taskArgs.template ] }) }); task("createDaoTemplate", "Creates a question template on the oracle address") - .addParam("oracle", "Address of the oracle (e.g. Realitio)", undefined, types.string) + .addParam("oracle", "Address of the oracle (e.g. RealitioV3)", undefined, types.string) .addParam( "template", "Template string for question (should include placeholders for proposal id and txs hash)", @@ -116,7 +125,7 @@ task("createDaoTemplate", "Creates a question template on the oracle address") .setAction(async (taskArgs, hardhatRuntime) => { const [caller] = await hardhatRuntime.ethers.getSigners(); console.log("Using the account:", caller.address); - const oracle = await hardhatRuntime.ethers.getContractAt("Realitio", taskArgs.oracle); + const oracle = await hardhatRuntime.ethers.getContractAt("RealitioV3", taskArgs.oracle); const receipt = await oracle.createTemplate(taskArgs.template).then((tx: any) => tx.wait()); const id = receipt.logs[0].topics[1] console.log("Template id:", id); diff --git a/yarn.lock b/yarn.lock index 04d802a..a39e072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -509,10 +509,10 @@ resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc" integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw== -"@gnosis.pm/zodiac@0.0.1-prealpha1": - version "0.0.1-prealpha1" - resolved "https://registry.yarnpkg.com/@gnosis.pm/zodiac/-/zodiac-0.0.1-prealpha1.tgz#02adcbd4d7c11aeffd01304bf7668e5e37fe18f4" - integrity sha512-SGi6TBCPribYH8++ooXDIPKbA/f/XY7yC3zPRXu3jNnjTGXGEj9Zv8RJUppc6GQgLL1a13Z6aALcRyMNGFbagQ== +"@gnosis.pm/zodiac@0.0.1-prealpha7": + version "0.0.1-prealpha7" + resolved "https://registry.yarnpkg.com/@gnosis.pm/zodiac/-/zodiac-0.0.1-prealpha7.tgz#0e648e45ca71cbe34fcc25e4c49332570c21d48b" + integrity sha512-BPgND+dozu3c0g0Tijm/DbYC/Xo51/EQHnCMN8yK1glogsM13BEkto8dshzTxXgwrRRyv6X46CGPLeNg7D56zQ== dependencies: "@gnosis.pm/mock-contract" "^4.0.0" "@gnosis.pm/safe-contracts" "1.3.0"