UNPKG

@gooddollar/goodprotocol

Version:
771 lines (670 loc) 23 kB
/** * deploy GoodDollar complete protocol from scratch */ import { network, ethers, upgrades } from "hardhat"; import { Contract } from "ethers"; import { range } from "lodash"; import DAOCreatorABI from "@gooddollar/goodcontracts/build/contracts/DaoCreatorGoodDollar.json"; import FeeFormulaABI from "@gooddollar/goodcontracts/build/contracts/FeeFormula.json"; import AddFoundersABI from "@gooddollar/goodcontracts/build/contracts/AddFoundersGoodDollar.json"; import ContributionCalculation from "@gooddollar/goodcontracts/stakingModel/build/contracts/ContributionCalculation.json"; import FirstClaimPool from "@gooddollar/goodcontracts/stakingModel/build/contracts/FirstClaimPool.json"; import BridgeMock from "@gooddollar/goodcontracts/stakingModel/build/contracts/BridgeMock.json"; import OTPABI from "@gooddollar/goodcontracts/build/contracts/OneTimePayments.json"; import HomeBridgeABI from "@gooddollar/goodcontracts/build/contracts/DeployHomeBridge.json"; import ForeignBridgeABI from "@gooddollar/goodcontracts/build/contracts/DeployForeignBridge.json"; import releaser from "../scripts/releaser"; import ProtocolSettings from "../releases/deploy-settings.json"; import dao from "../releases/deployment.json"; import { main as deployV2 } from "./upgradeToV2/upgradeToV2"; import { TransactionResponse } from "@ethersproject/providers"; import pressAnyKey from "press-any-key"; const { name } = network; const printDeploy = async ( c: Contract | TransactionResponse ): Promise<Contract | TransactionResponse> => { if (c instanceof Contract) { await c.deployed(); console.log("deployed to: ", c.address); } if (c.wait) { await c.wait(); console.log("tx done:", c.hash); } return c; }; export const createDAO = async () => { const fusedao = dao[network.name.split("-")[0]]; let release: { [key: string]: any } = {}; // let release: { [key: string]: any } = dao[network.name]; let [root, ...signers] = await ethers.getSigners(); //generic call permissions let schemeMock = root; const isMainnet = network.name.includes("main"); console.log("got signers:", { network, root: root.address, schemeMock: schemeMock.address, balance: await ethers.provider .getBalance(root.address) .then(_ => _.toString()) }); const DAOCreatorFactory = new ethers.ContractFactory( DAOCreatorABI.abi, DAOCreatorABI.bytecode, root ); // const IdentityFactory = new ethers.ContractFactory( // IdentityABI.abi, // IdentityABI.bytecode, // root // ); const IdentityFactory = await ethers.getContractFactory("IdentityV2"); const FeeFormulaFactory = new ethers.ContractFactory( FeeFormulaABI.abi, FeeFormulaABI.bytecode, root ); const AddFoundersFactory = new ethers.ContractFactory( AddFoundersABI.abi, AddFoundersABI.bytecode, root ); const BancorFormula = (await ( await ethers.getContractFactory("BancorFormula") ) .deploy() .then(printDeploy)) as Contract; await BancorFormula.init() const AddFounders = (await AddFoundersFactory.deploy().then( printDeploy )) as Contract; // const AddFounders = await ethers.getContractAt( // AddFoundersABI.abi, // "0x6F1BAbfF5E119d61F0c6d8653d84E8B284B87091" // ); // const Identity = (await IdentityFactory.deploy().then( // printDeploy // )) as Contract; const Identity = await upgrades.deployProxy( IdentityFactory, [root.address, ethers.constants.AddressZero], { kind: "uups" } ); // const Identity = await ethers.getContractAt( // IdentityABI.abi, // release.Identity // ); const daoCreator = (await DAOCreatorFactory.deploy(AddFounders.address).then( printDeploy )) as Contract; const FeeFormula = (await FeeFormulaFactory.deploy(0).then( printDeploy )) as Contract; // await Identity.setAuthenticationPeriod(365).then(printDeploy); // console.log("setAuthPeriod"); await daoCreator .forgeOrg( "GoodDollar", "G$", 0, FeeFormula.address, Identity.address, [root.address, signers[0].address, signers[1].address], 1000, [100000, 100000, 100000] ) .then(printDeploy); console.log("forgeOrg done "); const Avatar = new ethers.Contract( await daoCreator.avatar(), [ "function owner() view returns (address)", "function nativeToken() view returns (address)" ], root ); // const Avatar = new ethers.Contract( // release.Avatar, // [ // "function owner() view returns (address)", // "function nativeToken() view returns (address)" // ], // root // ); // await Identity.setAvatar(Avatar.address).then(printDeploy); console.log("Done deploying DAO, setting schemes permissions"); let schemes = [schemeMock.address, Identity.address]; const gd = await Avatar.nativeToken(); const controller = await Avatar.owner(); console.log("setting schemes", schemes); await daoCreator .setSchemes( Avatar.address, schemes, schemes.map(_ => ethers.constants.HashZero), ["0x0000001F", "0x00000001"], "" ) .then(printDeploy); let { setSchemes, genericCall, addWhitelisted } = await getHelperFunctions( Identity, Avatar, schemeMock ); let mainnet: { [key: string]: Contract } = {}; release = { ...release, Avatar: Avatar.address, Controller: controller, GoodDollar: gd, Identity: Identity.address, FeeFormula: FeeFormula.address, BancorFormula: BancorFormula.address }; if (isMainnet) { mainnet = await deployMainnet(Avatar, Identity); Object.entries(mainnet) .filter(([k, v]) => v) .forEach(([k, v]) => (release[k] = v.address)); } let sidechain: { [key: string]: any } = {}; if (false === isMainnet) { sidechain = await deploySidechain( setSchemes, genericCall, Avatar.address, Identity.address, gd ); schemes.push(sidechain.OneTimePayments.address); Object.entries(sidechain).forEach(([k, v]) => (release[k] = v.address)); } await releaser(release, network.name); const bridgeRelease = await deployBridge(Avatar, gd, setSchemes, isMainnet); release = { ...release, ...bridgeRelease }; await releaser(release, network.name); // await pressAnyKey(); // deploy v2 mainnet/sidechain contracts, returns their addresses const olddao = { FirstClaimPool: release.FirstClaimPool, BancorFormula: release.BancorFormula, Avatar: release.Avatar, Controller: release.Controller, DAIUsdOracle: release.DAIUsdOracle, COMPUsdOracle: release.COMPUsdOracle, USDCUsdOracle: release.USDCUsdOracle, AAVEUsdOracle: release.AAVEUsdOracle, AaveLendingPool: null, AaveIncentiveController: null, GasPriceOracle: release.GasPriceOracle, cDAI: release.cDAI || ethers.constants.AddressZero, DAI: release.DAI || ethers.constants.AddressZero, COMP: release.COMP || ethers.constants.AddressZero, USDC: ethers.constants.AddressZero, DAIEthOracle: release.DAIEthOracle || ethers.constants.AddressZero, ETHUsdOracle: release.ETHUsdOracle || ethers.constants.AddressZero, Identity: release.Identity, GoodDollar: release.GoodDollar, Contribution: release.Contribution, UniswapRouter: "0x0000000000000000000000000000000000000001", HomeBridge: release.HomeBridge, ForeignBridge: release.ForeignBridge, SchemeRegistrar: ethers.constants.AddressZero, UpgradeScheme: ethers.constants.AddressZero }; console.log("deploying v2..."); const v2 = await deployV2(network.name, false, olddao); if (isMainnet) { let distHelper = await upgrades.deployProxy( await ethers.getContractFactory("DistributionHelper"), [v2.NameService], { initializer: "initialize(address)", kind: "uups" } ); const goodReserve = await ethers.getContractFactory("GoodReserveCDai"); console.log("setting distribution helper..."); await genericCall( v2.GoodReserveCDai, goodReserve.interface.encodeFunctionData("setDistributionHelper", [ distHelper.address ]) ); } console.log("deploying adminWallet..."); const adminWallet = await deployAdminWallet(Identity.address, v2.NameService); console.log("setting up identity:", { ns: v2.NameService, identity: Identity.address }); await Identity.initDAO(v2.NameService); release["AdminWallet"] = adminWallet.address; release = { ...v2, ...release }; await releaser(release, network.name); // await pressAnyKey(); if (isMainnet) { await setSchemes([release.ProtocolUpgrade]); await performUpgrade(release, fusedao.UBIScheme); console.log("setting reserve token"); const MarketMaker = await ethers.getContractFactory("GoodMarketMaker"); let encoded = MarketMaker.interface.encodeFunctionData("initializeToken", [ release.cDAI, "100", "10000", "1000000", "0" ]); await genericCall(release.GoodMarketMaker, encoded); } if (false === isMainnet) { let encoded = ( await ethers.getContractAt("IGoodDollar", release.GoodDollar) ).interface.encodeFunctionData("mint", [release.UBIScheme, 1000000]); await genericCall(release.GoodDollar, encoded); await setSchemes([release.ProtocolUpgradeFuse]); await performUpgradeFuse(release); } await releaser(release, network.name); }; const deployBridge = async (Avatar, gd, setSchemes, isMainnet) => { const GoodDollar = await ethers.getContractAt("IGoodDollar", gd); const [root] = await ethers.getSigners(); const BridgeABI = isMainnet ? ForeignBridgeABI : HomeBridgeABI; const bridgeFactory = new ethers.ContractFactory( BridgeABI.abi, BridgeABI.bytecode, root ); let BridgeFactoryContract = isMainnet ? "0xABBf5D8599B2Eb7b4e1D25a1Fd737FF1987655aD" : "0xb895638fb3870AD5832402a5BcAa64A044687db0"; //Fuse test bridge addresses const isAlreadyMinter = await GoodDollar.isMinter(BridgeFactoryContract); console.log("deploying bridge scheme:", { BridgeFactoryContract, isMainnet, isAlreadyMinter }); const scheme = (await bridgeFactory .deploy(Avatar.address, BridgeFactoryContract) .then(printDeploy)) as Contract; await setSchemes([scheme.address]); if (network.name.includes("develop") || network.name.includes("dapptest")) { const mockBridge = (await new ethers.ContractFactory( BridgeMock.abi, BridgeMock.bytecode, root ) .deploy() .then(printDeploy)) as Contract; console.log("deployed mock bridge for develop mode:", mockBridge.address); return isMainnet ? { ForeignBridge: mockBridge.address } : { HomeBridge: mockBridge.address }; } let tx = await ( await (isMainnet ? scheme.setBridge() : scheme.setBridge(isAlreadyMinter === false)) ).wait(); const bridgeEvent = tx.events.find(_ => _.event?.includes("Bridge")); console.log("deployed bridge:", bridgeEvent); console.log(tx.events, tx); return isMainnet ? { ForeignBridge: bridgeEvent.args._foreignBridge } : { HomeBridge: bridgeEvent.args._homeBridge }; }; const deployMainnet = async (Avatar, Identity) => { console.log("deploying mainnet..."); const [root] = await ethers.getSigners(); const cdaiFactory = await ethers.getContractFactory("cDAIMock"); const daiFactory = await ethers.getContractFactory("DAIMock"); const daiAddr = ProtocolSettings[network.name]?.compound?.dai; const cdaiAddr = ProtocolSettings[network.name]?.compound?.cdai; const COMPAddr = ProtocolSettings[network.name]?.compound?.comp; const daiEthOracleAddr = ProtocolSettings[network.name]?.chainlink?.dai_eth; const ethUsdOracleAddr = ProtocolSettings[network.name]?.chainlink?.eth_usd; let DAIUsdOracle, COMPUsdOracle, USDCUsdOracle, AAVEUsdOracle, GasPriceOracle; if (name.includes("test")) { const tokenUsdOracleFactory = await ethers.getContractFactory( "BatUSDMockOracle" ); DAIUsdOracle = await tokenUsdOracleFactory.deploy(); USDCUsdOracle = await tokenUsdOracleFactory.deploy(); COMPUsdOracle = await ( await ethers.getContractFactory("CompUSDMockOracle") ).deploy(); AAVEUsdOracle = await ( await ethers.getContractFactory("AaveUSDMockOracle") ).deploy(); GasPriceOracle = await ( await ethers.getContractFactory("GasPriceMockOracle") ).deploy(); } let DAI = daiAddr ? await ethers.getContractAt("DAIMock", daiAddr) : ((await daiFactory.deploy().then(printDeploy)) as Contract); let COMP = COMPAddr ? await ethers.getContractAt("DAIMock", COMPAddr) : ((await daiFactory.deploy().then(printDeploy)) as Contract); let cDAI = cdaiAddr ? await ethers.getContractAt("DAIMock", cdaiAddr) : ((await cdaiFactory.deploy(DAI.address).then(printDeploy)) as Contract); const ccFactory = new ethers.ContractFactory( ContributionCalculation.abi, ContributionCalculation.bytecode, root ); const Contribution = (await ccFactory .deploy(Avatar.address, 0, 1e15) .then(printDeploy)) as Contract; let DAIEthOracle = daiEthOracleAddr ? await ethers.getContractAt("DaiEthPriceMockOracle", daiEthOracleAddr) : ((await (await ethers.getContractFactory("DaiEthPriceMockOracle")) .deploy() .then(printDeploy)) as Contract); let ETHUsdOracle = ethUsdOracleAddr ? await ethers.getContractAt("EthUSDMockOracle", ethUsdOracleAddr) : ((await (await ethers.getContractFactory("EthUSDMockOracle")) .deploy() .then(printDeploy)) as Contract); return { Contribution, DAI, COMP, cDAI, ETHUsdOracle, DAIEthOracle, DAIUsdOracle, COMPUsdOracle, USDCUsdOracle, AAVEUsdOracle, GasPriceOracle }; }; export const deploySidechain = async ( setSchemes, genericCall, avatar, identity, gd ) => { console.log("deploying sidechain..."); const root = (await ethers.getSigners())[0]; const fcFactory = new ethers.ContractFactory( FirstClaimPool.abi, FirstClaimPool.bytecode, root ); const otpf = await new ethers.ContractFactory( OTPABI.abi, OTPABI.bytecode, root ); // const invitesf = await new ethers.ContractFactory( // InvitesABI.abi, // InvitesABI.bytecode, // root // ); const invitesf = await ethers.getContractFactory("InvitesV1"); const faucetf = await ethers.getContractFactory("FuseFaucet"); // const faucetf = await new ethers.ContractFactory( // FaucetABI.abi, // FaucetABI.bytecode, // root // ); console.log("deploying first claim...", { avatar, identity }); const firstClaim = (await fcFactory .deploy(avatar, identity, 1000) .then(printDeploy)) as Contract; let encoded = ( await ethers.getContractAt("IGoodDollar", gd) ).interface.encodeFunctionData("mint", [firstClaim.address, 1000000]); await genericCall(gd, encoded); console.log("deploying OneTimePayments"); const otp = (await otpf .deploy(avatar, identity) .then(printDeploy)) as Contract; console.log("deploying OneTimePayments invites"); const invites = (await upgrades .deployProxy(invitesf, [avatar, identity, gd, 10000]) .then(printDeploy)) as Contract; const faucet = (await upgrades .deployProxy(faucetf, [identity]) .then(printDeploy)) as Contract; await root .sendTransaction({ to: faucet.address, value: ethers.utils.parseEther("5") }) .then(printDeploy); encoded = ( await ethers.getContractAt("IGoodDollar", gd) ).interface.encodeFunctionData("mint", [firstClaim.address, 1000000]); await genericCall(gd, encoded); encoded = ( await ethers.getContractAt("IGoodDollar", gd) ).interface.encodeFunctionData("mint", [invites.address, 1000000]); await genericCall(gd, encoded); console.log("setting firstclaim and otp schemes..."); await setSchemes([firstClaim.address, otp.address]); await firstClaim.start().then(printDeploy); return { FirstClaimPool: firstClaim, OneTimePayments: otp, Invites: invites, FuseFaucet: faucet }; }; const deployAdminWallet = async (identity, ns) => { const root = (await ethers.getSigners())[0]; const hdNode = ethers.utils.HDNode.fromMnemonic(process.env.ADMIN_MNEMONIC); const admins = range(0, 50).map(i => hdNode.derivePath(`m/44'/60'/0'/0/${i}`) ); const adminWallet = (await upgrades .deployProxy(await ethers.getContractFactory("AdminWallet"), [ admins.slice(0, 20).map(_ => _.address), ns, root.address, 1e9 ]) .then(printDeploy)) as Contract; // const adminWallet = (await new ethers.ContractFactory( // AdminWalletABI.abi, // AdminWalletABI.bytecode, // root // ) // .deploy( // admins.slice(0, 20).map(_ => _.address), // ethers.utils.parseUnits("1000000", "gwei"), // 4, // identity // ) // .then(printDeploy)) as Contract; const id = await ethers.getContractAt("IdentityV2", identity); await id .grantRole(await id.IDENTITY_ADMIN_ROLE(), adminWallet.address) .then(printDeploy); await root .sendTransaction({ to: adminWallet.address, value: ethers.utils.parseEther("1") }) .then(printDeploy); await adminWallet["topAdmins(uint256)"](0).then(printDeploy); console.log( "deployAdminWallet admins:", admins.map(_ => _.address), "balance:", await ethers.provider.getBalance(adminWallet.address) ); return adminWallet; }; const getHelperFunctions = async (Identity, Avatar, schemeMock) => { const controller = await Avatar.owner(); const Controller = await ethers.getContractAt( "Controller", controller, schemeMock ); const setSchemes = async (addrs, params = []) => { for (let i in addrs) { await Controller.registerScheme( addrs[i], params[i] || ethers.constants.HashZero, "0x0000001F", Avatar.address ).then(printDeploy); } }; const genericCall = (target, encodedFunc) => { return Controller.genericCall(target, encodedFunc, Avatar.address, 0).then( printDeploy ); }; const addWhitelisted = (addr, did, isContract = false) => { if (isContract) return Identity.addContract(addr); return Identity.addWhitelistedWithDID(addr, did).then(printDeploy); }; return { setSchemes, addWhitelisted, genericCall }; }; const performUpgradeFuse = async release => { const upgrade = await ethers.getContractAt( "ProtocolUpgradeFuse", release.ProtocolUpgradeFuse ); console.log("performing protocol v2 upgrade on Fuse...", { release }); await upgrade .upgrade( release.NameService, //old contracts [ ethers.constants.AddressZero, ethers.constants.AddressZero, ethers.constants.AddressZero, release.FirstClaimPool ], release.UBIScheme, [ ethers.utils.keccak256(ethers.utils.toUtf8Bytes("REPUTATION")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("BRIDGE_CONTRACT")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("UBISCHEME")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("GDAO_STAKING")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("GDAO_CLAIMERS")) ], [ release.GReputation, release.HomeBridge || ethers.constants.AddressZero, release.UBIScheme, release.GovernanceStaking, release.ClaimersDistribution ] ) .then(printDeploy); console.log("upgrading governance..."); await upgrade .upgradeGovernance( ethers.constants.AddressZero, ethers.constants.AddressZero, release.CompoundVotingMachine ) .then(printDeploy); }; const performUpgrade = async (release, ubiScheme) => { const upgrade = await ethers.getContractAt( "ProtocolUpgrade", release.ProtocolUpgrade ); console.log("performing protocol v2 upgrade on Mainnet...", { release }); console.log( "upgrading nameservice + staking rewards...", release.NameService, [ ethers.utils.keccak256(ethers.utils.toUtf8Bytes("RESERVE")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MARKET_MAKER")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("FUND_MANAGER")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("REPUTATION")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("GDAO_STAKERS")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("BRIDGE_CONTRACT")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("UBI_RECIPIENT")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("EXCHANGE_HELPER")) ], [ release.GoodReserveCDai, release.GoodMarketMaker, release.GoodFundManager, release.GReputation, release.StakersDistribution, release.ForeignBridge || ethers.constants.AddressZero, ubiScheme, release.ExchangeHelper ], release.StakingContracts.map((_: any) => _[0]), release.StakingContracts.map((_: any) => _[1]) ); let tx; tx = await upgrade .upgradeBasic( release.NameService, [ ethers.utils.keccak256(ethers.utils.toUtf8Bytes("RESERVE")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("MARKET_MAKER")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("FUND_MANAGER")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("REPUTATION")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("GDAO_STAKERS")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("BRIDGE_CONTRACT")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("UBI_RECIPIENT")), ethers.utils.keccak256(ethers.utils.toUtf8Bytes("EXCHANGE_HELPER")) ], [ release.GoodReserveCDai, release.GoodMarketMaker, release.GoodFundManager, release.GReputation, release.StakersDistribution, release.ForeignBridge || ethers.constants.AddressZero, ubiScheme, release.ExchangeHelper ], release.StakingContracts.map((_: any) => _[0]), release.StakingContracts.map((_: any) => _[1]) ) .then(printDeploy); console.log("upgrading reserve...", { params: [ release.NameService, ethers.constants.AddressZero, ethers.constants.AddressZero, ethers.constants.AddressZero, release.COMP ] }); tx = await upgrade .upgradeReserve( release.NameService, ethers.constants.AddressZero, ethers.constants.AddressZero, ethers.constants.AddressZero, release.COMP ) .then(printDeploy); console.log("upgrading governance..."); tx = await upgrade .upgradeGovernance( ethers.constants.AddressZero, ethers.constants.AddressZero, release.CompoundVotingMachine ) .then(printDeploy); }; const main = async () => { await createDAO(); }; main().catch(e => console.log(e));