diff --git a/.gitignore b/.gitignore index 9876e5a..09a856b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ store.json manifesto.json nftAbi.json govAbi.json +.DS_Store #Hardhat files cache diff --git a/README.md b/README.md index 66935d9..40f4a35 100644 --- a/README.md +++ b/README.md @@ -39,18 +39,26 @@ cp .env.template .env Add your own keys in your `.env` file. +- Configure your DAO in the `dao.config.ts` file, [the docs is here if you need](https://w3hc.github.io/gov-docs/) +- Edit the manifesto in `storage/manifesto/manifesto.md` +- Replace the image (membership NFT image) in `storage/metadata/image.png` + Then deploy to Goerli: -```js -npm run deploy +```bash +npm run deploy:goerli ``` -or +or deploy to Arthera +```bash +npm run deploy:arthera ``` -npx hardhat run scripts/clear.ts -npx hardhat run scripts/deploy-nft.ts --network goerli -npx hardhat run scripts/deploy-gov.ts --network goerli + +or deploy to Optimism Goerli: + +```bash +npm run deploy:og ``` Then you can interact with your DAO using [Tally](https://www.tally.xyz/). @@ -61,16 +69,16 @@ To upload the membership NFT metadata, upload the manifesto, or submit a proposa ## Supported networks -- Optimism Mainnet -- Goerli Testnet -- Optimism Goerli Testnet +- Optimism Mainnet ([view W3HC DAO contract](https://optimistic.etherscan.io/address/0x83e2403a8b94af988b4f4ae9869577783b8cd216#writeContract)) +- Goerli Testnet ([view latest deployment](https://goerli.etherscan.io/address/0x4Ab5851BaAA670f93CE5a1B1E4885eBe12FD4f1d#writeContract)) +- Optimism Goerli Testnet ([view latest deployment](https://goerli-optimism.etherscan.io/address/0xa2be3b1b4666ceb06c3237078b73089b8b95078c#writeContract)) - Arbitrum Goerli Testnet - Celo Alfajores Testnet - Celo Mainnet - Gnosis Chiado Testnet - Gnosis Mainnet - Mantle Testnet -- Arthera Testnet +- Arthera Testnet ([view latest deployment](https://explorer-test.arthera.net/address/0x28F1Ef960E2674cAdf2F4197910e2fcFb4b8BA1C?tab=txs)) ## Security diff --git a/dao.config.ts b/dao.config.ts new file mode 100644 index 0000000..17484e5 --- /dev/null +++ b/dao.config.ts @@ -0,0 +1,25 @@ +export const firstMembers = [ + "0xD8a394e7d7894bDF2C57139fF17e5CBAa29Dd977", // First members. Super important. + "0xe61A1a5278290B6520f0CEf3F2c71Ba70CF5cf4C" +]; + +// DAO config +export const daoName = "Our DAO" // Give your DAO a name. +export const votingDelay = 1 // Just 1 block voting delay by default. +export const votingPeriod = 300 // Depends on the network you're deploying to. +export const quorum = 20 // Classic 20% quorum by default. +export const votingThreshold = 0 // 0 allows non-members to submit proposals. + +// NFT config +export const nftSymbol = "MEMBER" // NFT symbol +export const nftDescription = "This is us." // Nft description + +// set to false if you don't want any specific attributes to the membership NFT +export const attributes = true + +// Attributes: relevant only when attributes is true +export const participationRate = "100%" +export const daoAddress = "unset" +export const nickname = "unset" +export const contribs = 0 +export const role = "Hacker" \ No newline at end of file diff --git a/package.json b/package.json index 0532ac5..405f983 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ }, "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "deploy": "npx hardhat run scripts/clear.ts && npx hardhat run scripts/deploy-nft.ts --network goerli && npx hardhat run scripts/deploy-gov.ts --network goerli", + "test": "hardhat test", + "deploy:goerli": "npm run clear && hardhat run scripts/deploy-nft.ts --network goerli && hardhat run scripts/deploy-gov.ts --network goerli", + "deploy:arthera": "npm run clear && hardhat run scripts/deploy-nft.ts --network arthera-testnet && hardhat run scripts/deploy-gov.ts --network arthera-testnet", + "deploy:og": "npm run clear && hardhat run scripts/deploy-nft.ts --network optimism-goerli && hardhat run scripts/deploy-gov.ts --network optimism-goerli", "clear": "npx hardhat run scripts/clear.ts" }, "keywords": [], diff --git a/scripts/deploy-eur.ts b/scripts/deploy-eur.ts index dbc5128..2a1a88e 100644 --- a/scripts/deploy-eur.ts +++ b/scripts/deploy-eur.ts @@ -17,13 +17,15 @@ async function main() { console.log("\nEUR deployed at", msg(eur.address), "✅") const receipt = await ethers.provider.getTransactionReceipt(eur.deployTransaction.hash) - try { - console.log("\nEtherscan verification in progress...") - await eur.deployTransaction.wait(6) - await hre.run("verify:verify", { network: network.name, address: eur.address, constructorArguments: [], }) - console.log("Etherscan verification done. ✅") - } catch (error) { - console.error(error) + if (network.name !== 'arthera-testnet') { + try { + console.log("\nEtherscan verification in progress...") + await eur.deployTransaction.wait(6) + await hre.run("verify:verify", { network: network.name, address: eur.address, constructorArguments: [], }) + console.log("Etherscan verification done. ✅") + } catch (error) { + console.error(error) + } } } diff --git a/scripts/deploy-gov.ts b/scripts/deploy-gov.ts index 7356ca4..6ba6596 100644 --- a/scripts/deploy-gov.ts +++ b/scripts/deploy-gov.ts @@ -3,6 +3,7 @@ import * as store from '../store.json' import fs from 'fs' const color = require("cli-color") import { upload } from '../scripts/upload-manifesto' +import { daoName, votingDelay, votingPeriod, quorum, votingThreshold } from "../dao.config" var msg = color.xterm(39).bgXterm(128) @@ -14,22 +15,30 @@ async function main() { const manifesto = await upload() console.log("\nManifesto CID:", manifesto, "✅") - // Edit the following 5 variables - const name = "Our DAO" - const votingDelay = 1 - const votingPeriod = 300 - const votingThreshold = 1 - const quorum = 20 + let gov + if (network.name !== 'optimism-goerli') { - const gov = await Gov.deploy( - store.nft, - manifesto, - name, - votingDelay, - votingPeriod, - votingThreshold, - quorum - ) + gov = await Gov.deploy( + store.nft, + manifesto, + daoName, + votingDelay, + votingPeriod, + votingThreshold, + quorum + ) + + } else { + gov = await Gov.deploy( + store.nft, + manifesto, + daoName, + votingDelay, + votingPeriod, + votingThreshold, + quorum + ) + } await gov.deployed() console.log("\nGov deployed at", msg(gov.address), "✅") const receipt = await ethers.provider.getTransactionReceipt(gov.deployTransaction.hash) @@ -53,23 +62,24 @@ async function main() { ) console.log("\nGov ABI available in govAbi.json ✅") - try { - console.log("\nEtherscan verification in progress...") - await gov.deployTransaction.wait(6) - await hre.run("verify:verify", { network: network.name, address: gov.address, constructorArguments: [ - store.nft, - manifesto, - name, - votingDelay, - votingPeriod, - votingThreshold, - quorum], - }); - console.log("Etherscan verification done. ✅") - } catch (error) { - console.error(error); + if (network.name !== 'arthera-testnet') { + try { + console.log("\nEtherscan verification in progress...") + await gov.deployTransaction.wait(6) + await hre.run("verify:verify", { network: network.name, address: gov.address, constructorArguments: [ + store.nft, + manifesto, + daoName, + votingDelay, + votingPeriod, + votingThreshold, + quorum], + }); + console.log("Etherscan verification done. ✅") + } catch (error) { + console.error(error); + } } - const [issuer] = await ethers.getSigners() const abiDir = __dirname + '/../artifacts/contracts'; const nftAbiContract = abiDir + "/" + "NFT.sol" + "/" + "NFT" + ".json" diff --git a/scripts/deploy-nft.ts b/scripts/deploy-nft.ts index 73713d7..d161d34 100644 --- a/scripts/deploy-nft.ts +++ b/scripts/deploy-nft.ts @@ -5,6 +5,7 @@ const color = require("cli-color") var msg = color.xterm(39).bgXterm(128); import { Web3Storage, Blob, File , getFilesFromPath } from "web3.storage" import * as dotenv from "dotenv" +import { firstMembers, daoName, nftSymbol, nftDescription, participationRate, contribs, daoAddress, nickname, role, attributes } from "../dao.config" dotenv.config(); var msg = color.xterm(39).bgXterm(128) @@ -13,16 +14,6 @@ async function main() { console.log("\nStorage in progress...") - const firstMembers = [ - "0x718d0218857f2B6f6e3D171eC3465d7bBf8daD9B", - "0x466c3c7c3bC3e9153Be21867d73782F02708B45B", - "0x382AB35697B69a6C08BA86d379583d7f39Fd4941", - "0x476E2651BF97dE8a26e4A05a9c8e00A6EFa1390c", - "0x27292E1a901E3E0bE7d144aDba4CAD07da2d8a42", - "0x5a5dA6353aA78B0f97e69DA11E3ae022df03456e", - "0x7fd5d090a30460385dd165fc1608e1f66fa2448e" - ]; - function getAccessToken() { return process.env.WEB3STORAGE_TOKEN } @@ -32,12 +23,12 @@ async function main() { } const dir = "./storage/metadata/" - let name:string = "" + let fileName:string = "" async function getFiles (file:any) { const File = await getFilesFromPath(file) - name = File[File.length -1].name.substring(10) - console.log("name:", name) + fileName = File[File.length -1].name.substring(10) + console.log("fileName:", fileName) return File } @@ -48,39 +39,43 @@ async function main() { } const cid = await storeFiles(await getFiles(dir)) - console.log("url:", "https://" + cid + ".ipfs.w3s.link/"+ name) + console.log("url:", "https://" + cid + ".ipfs.w3s.link/"+ fileName) console.log("\ncid:", cid) + const nftName = daoName + " Membership" - // Edit the following variable - const metadata = { - "name": "DAO Membership", - "author": "Gov", - "description": - "The owner of this NFT has a right to vote on the test DAO proposals.", - "image": "ipfs://" + cid + "/" + name, - "attributes": [ + let metaLabs + if (attributes === true) { + metaLabs = [ { "trait_type": "Participation rate (%)", - "value": "100", + "value": participationRate, }, { "trait_type": "Contribs", - "value": "0", + "value": contribs, }, { "trait_type": "DAO", - "value": "unset", + "value": daoAddress, }, { "trait_type": "Nickname", - "value": "unset", + "value": nickname, }, { "trait_type": "Role", - "value": "Hacker", + "value": role, }, - ], + ] + } else metaLabs = [] + + // Edit the following variable + const metadata = { + "name": nftName, + "description": nftDescription, + "image": "ipfs://" + cid + "/" + fileName, + "attributes": [], }; function makeFileObjects() { @@ -106,9 +101,7 @@ async function main() { console.log("\nNFT deployment in progress...") const NFT = await ethers.getContractFactory("NFT") - const nftName = "Membership NFT" - const symbol = "MEMBER" - const nft = await NFT.deploy(firstMembers, uri, nftName, symbol) + const nft = await NFT.deploy(firstMembers, uri, nftName, nftSymbol) await nft.deployed() console.log("\nNFT deployed at", msg(nft.address), "✅") const receipt = await ethers.provider.getTransactionReceipt(nft.deployTransaction.hash) @@ -129,16 +122,17 @@ async function main() { ) console.log("\nNFT ABI available in nftAbi.json ✅") - try { - console.log("\nEtherscan verification in progress...") - await nft.deployTransaction.wait(6) - await hre.run("verify:verify", { network: network.name, address: nft.address, constructorArguments: [firstMembers, uri, nftName, symbol], }) - console.log("Etherscan verification done. ✅") - } catch (error) { - console.error(error) + if (network.name !== 'arthera-testnet') { + try { + console.log("\nEtherscan verification in progress...") + await nft.deployTransaction.wait(6) + await hre.run("verify:verify", { network: network.name, address: nft.address, constructorArguments: [firstMembers, uri, nftName, nftSymbol], }) + console.log("Etherscan verification done. ✅") + } catch (error) { + console.error(error) + } } } - main().catch((error) => { console.error(error) process.exitCode = 1