UNPKG

@gooddollar/goodprotocol

Version:
759 lines (658 loc) 22.9 kB
import { ethers, upgrades } from "hardhat"; import { expect } from "chai"; import DAOCreatorABI from "@gooddollar/goodcontracts/build/contracts/DaoCreatorGoodDollarWithTokens.json"; import FeeFormulaABI from "@gooddollar/goodcontracts/build/contracts/FeeFormula.json"; import ContributionCalculation from "@gooddollar/goodcontracts/stakingModel/build/contracts/ContributionCalculation.json"; import FirstClaimPool from "@gooddollar/goodcontracts/stakingModel/build/contracts/FirstClaimPool.json"; import SchemeRegistrar from "@gooddollar/goodcontracts/build/contracts/SchemeRegistrar.json"; import AbsoluteVote from "@gooddollar/goodcontracts/build/contracts/AbsoluteVote.json"; import UpgradeScheme from "@gooddollar/goodcontracts/build/contracts/UpgradeScheme.json"; import UBIScheme from "@gooddollar/goodcontracts/stakingModel/build/contracts/UBIScheme.json"; import IUniswapV2Pair from "@uniswap/v2-core/build/IUniswapV2Pair.json"; import UniswapV2Factory from "@uniswap/v2-core/build/UniswapV2Factory.json"; import WETH9 from "@uniswap/v2-periphery/build/WETH9.json"; import UniswapV2Router02 from "@uniswap/v2-periphery/build/UniswapV2Router02.json"; import { GoodMarketMaker, CompoundVotingMachine } from "../types"; import { Contract } from "ethers"; import testDeployer from "@superfluid-finance/ethereum-contracts/dev-scripts/deploy-test-framework"; export const getStakingFactory = async ( factory: | "GoodCompoundStaking" | "GoodAaveStaking" | "GoodCompoundStakingTest" | "GoodAaveStakingV2" | "GoodCompoundStakingV2" ) => { let swapHelper = await ethers .getContractFactory("UniswapV2SwapHelper") .then(_ => _.deploy()); const simpleStakingFactory = await ethers.getContractFactory(factory, { libraries: { UniswapV2SwapHelper: swapHelper.address } }); return simpleStakingFactory; }; export const deploySuperFluid = async () => { // This deploys the whole framework with various contracts const { frameworkDeployer } = await testDeployer.deployTestFramework(); // returns contract addresses as a struct, see https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/utils/SuperfluidFrameworkDeployer.sol#L48 const contractsFramework = await frameworkDeployer.getFramework(); return contractsFramework; }; export const deploySuperGoodDollar = async (sfContracts, tokenArgs) => { const SuperGoodDollarFactory = await ethers.getContractFactory( "SuperGoodDollar" ); console.log("deploying supergooddollar logic"); const SuperGoodDollar = await SuperGoodDollarFactory.deploy(sfContracts.host); console.log("deploying supergooddollar proxy"); const GoodDollarProxyFactory = await ethers.getContractFactory( "contracts/token/superfluid/UUPSProxy.sol:UUPSProxy" ); const GoodDollarProxy = await GoodDollarProxyFactory.deploy(); console.log("deploying flow nfts..."); const outNftProxy = await GoodDollarProxyFactory.deploy(); const inNftProxy = await GoodDollarProxyFactory.deploy(); const constantInflowNFT = await ethers.deployContract("ConstantInflowNFT", [ sfContracts.host, outNftProxy.address ]); const constantOutflowNFT = await ethers.deployContract("ConstantOutflowNFT", [ sfContracts.host, inNftProxy.address ]); await outNftProxy.initializeProxy(constantOutflowNFT.address); await inNftProxy.initializeProxy(constantInflowNFT.address); console.log("deployed supergooddollar proxy, initializing proxy..."); await GoodDollarProxy.initializeProxy(SuperGoodDollar.address); console.log("initializing supergooddollar...."); await SuperGoodDollar.attach(GoodDollarProxy.address)[ "initialize(string,string,uint256,address,address,address,address,address,address)" ](...tokenArgs, outNftProxy.address, inNftProxy.address); const GoodDollar = await ethers.getContractAt( "SuperGoodDollar", GoodDollarProxy.address ); console.log("supergooddollar created successfully"); await constantOutflowNFT .attach(outNftProxy.address) .initialize( (await GoodDollar.symbol()) + " Outflow NFT", (await GoodDollar.symbol()) + " COF" ); await constantInflowNFT .attach(inNftProxy.address) .initialize( (await GoodDollar.symbol()) + " Inflow NFT", (await GoodDollar.symbol()) + " CIF" ); return GoodDollar; }; export const createDAO = async (tokenType: "super" | "regular" = "super") => { let [root, ...signers] = await ethers.getSigners(); const sfContracts = await deploySuperFluid(); const cdaiFactory = await ethers.getContractFactory("cDAIMock"); const daiFactory = await ethers.getContractFactory("DAIMock"); let dai = await daiFactory.deploy(); let COMP = await daiFactory.deploy(); let cDAI = await cdaiFactory.deploy(dai.address); const DAOCreatorFactory = new ethers.ContractFactory( DAOCreatorABI.abi, DAOCreatorABI.bytecode, root ); const IdentityFactory = await ethers.getContractFactory("IdentityV2"); const FeeFormulaFactory = new ethers.ContractFactory( FeeFormulaABI.abi, FeeFormulaABI.bytecode, root ); const BancorFormula = await ( await ethers.getContractFactory("BancorFormula") ).deploy(); await BancorFormula.init(); console.log("deploy upgradeable identity..."); const Identity = await upgrades.deployProxy( IdentityFactory, [root.address, ethers.constants.AddressZero], { kind: "uups" } ); const daoCreator = await DAOCreatorFactory.deploy(); const FeeFormula = await FeeFormulaFactory.deploy(0); const GReputation = await ethers.getContractFactory("GReputation"); console.log("deploy upgradeable rep..."); let reputation = await upgrades.deployProxy( GReputation, [ethers.constants.AddressZero, "", ethers.constants.HashZero, 0], { kind: "uups", initializer: "initialize(address, string, bytes32, uint256)" } ); let GoodDollar; if (tokenType === "regular") { console.log("deploy regular G$..."); const GoodDollarFactory = await ethers.getContractFactory("GoodDollar"); GoodDollar = await upgrades.deployProxy( GoodDollarFactory, [ "GoodDollar", "G$", 0, FeeFormula.address, Identity.address, ethers.constants.AddressZero, daoCreator.address ], { kind: "uups", initializer: "initialize(string, string, uint256, address, address, address,address)" } ); } else { console.log("deploy super G$..."); GoodDollar = await deploySuperGoodDollar(sfContracts, [ "GoodDollar", "G$", 0, // cap FeeFormula.address, Identity.address, ethers.constants.AddressZero, daoCreator.address ]); } console.log("creating DAO...", { gdOwner: await GoodDollar.owner(), gd: GoodDollar.address, GOOD: reputation.address, daoCreator: daoCreator.address }); // await Identity.setAuthenticationPeriod(365); await daoCreator.forgeOrg( GoodDollar.address, reputation.address, [], 1000, [] ); const Avatar = new ethers.Contract( await daoCreator.avatar(), [ "function owner() view returns (address)", "function nativeToken() view returns (address)" ], root ); // await Identity.setAvatar(Avatar.address); const controller = await Avatar.owner(); console.log( "is controller/avatar minters and pauser", await GoodDollar.isMinter(controller), await GoodDollar.isMinter(Avatar.address), await GoodDollar.isPauser(Avatar.address) ); const ccFactory = new ethers.ContractFactory( ContributionCalculation.abi, ContributionCalculation.bytecode, root ); const contribution = await ccFactory.deploy(Avatar.address, 0, 1e15); console.log("deploying nameService", [ controller, Avatar.address, Identity.address, await Avatar.nativeToken(), contribution.address, BancorFormula.address, dai.address, cDAI.address ]); const nameService = await upgrades.deployProxy( await ethers.getContractFactory("NameService"), [ controller, [ "CONTROLLER", "AVATAR", "IDENTITY", "GOODDOLLAR", "CONTRIBUTION_CALCULATION", "BANCOR_FORMULA", "DAI", "CDAI", "COMP", "UBISCHEME", "BRIDGE_CONTRACT", "UBI_RECIPIENT" ].map(_ => ethers.utils.keccak256(ethers.utils.toUtf8Bytes(_))), [ controller, Avatar.address, Identity.address, await Avatar.nativeToken(), contribution.address, BancorFormula.address, dai.address, cDAI.address, COMP.address, root.address, root.address, root.address ] ], { kind: "uups" } ); await Identity.initDAO(nameService.address); console.log("deploying reserve..."); let goodReserve = await upgrades.deployProxy( await ethers.getContractFactory("GoodReserveCDai"), [ nameService.address, //check sample merkle tree generated by gdxAirdropCalculation.ts script "0x26ef809f3f845395c0bc66ce1eea85146516cb99afd030e2085b13e79514e94c" ], { initializer: "initialize(address, bytes32)", kind: "uups" } ); console.log("deploying disthelper..."); let distHelper = await upgrades.deployProxy( await ethers.getContractFactory("DistributionHelper"), [nameService.address], { initializer: "initialize(address)", kind: "uups" } ); console.log("deploying marketMaker..."); const MM = await ethers.getContractFactory("GoodMarketMaker"); let marketMaker = (await upgrades.deployProxy( MM, [nameService.address, 999388834642296, 1e15], { kind: "uups" } )) as unknown as GoodMarketMaker; await (await reputation.updateDAO(nameService.address)).wait(); console.log("Done deploying DAO, setting up nameService..."); //generic call permissions let schemeMock = signers[signers.length - 1]; const ictrl = await ethers.getContractAt( "Controller", controller, schemeMock ); const setSchemes = async (addrs, params = []) => { for (let i in addrs) { await ictrl.registerScheme( addrs[i], params[i] || ethers.constants.HashZero, "0x0000001F", Avatar.address ); } }; const setDAOAddress = async (name, addr) => { const encoded = nameService.interface.encodeFunctionData("setAddress", [ name, addr ]); await ictrl.genericCall(nameService.address, encoded, Avatar.address, 0); }; const runAsAvatarOnly = async (contract, functionAbi, ...parameters) => { const funcNameEnd = functionAbi.indexOf("("); expect(funcNameEnd).to.be.gt(-1); const functionName = functionAbi.substring(0, funcNameEnd); await expect(contract[functionAbi](...parameters)).to.revertedWith( /avatar/ ); const encoded = contract.interface.encodeFunctionData(functionName, [ ...parameters ]); await ictrl.genericCall(contract.address, encoded, Avatar.address, 0); }; const setReserveToken = async (token, gdReserve, tokenReserve, RR) => { const encoded = marketMaker.interface.encodeFunctionData( "initializeToken", [token, gdReserve, tokenReserve, RR, 0] ); await ictrl.genericCall(marketMaker.address, encoded, Avatar.address, 0); }; const genericCall = (target, encodedFunc) => { return ictrl.genericCall(target, encodedFunc, Avatar.address, 0); }; const addWhitelisted = (addr, did, isContract = false) => { if (isContract) return Identity.addContract(addr); return Identity.addWhitelistedWithDID(addr, did); }; console.log("Setting schemes..."); await daoCreator.setSchemes( Avatar.address, [schemeMock.address, Identity.address], [ethers.constants.HashZero, ethers.constants.HashZero], ["0x0000001F", "0x0000001F"], "" ); const gd = await Avatar.nativeToken(); //make GoodCap minter console.log("Setting reserve as minter..."); const encoded = ( await ethers.getContractAt("IGoodDollar", gd) ).interface.encodeFunctionData("addMinter", [goodReserve.address]); await ictrl.genericCall(gd, encoded, Avatar.address, 0); console.log("Setting reserve distribution helper..."); await ictrl.genericCall( goodReserve.address, goodReserve.interface.encodeFunctionData("setDistributionHelper", [ distHelper.address ]), Avatar.address, 0 ); const gasFeeMockFactory = await ethers.getContractFactory( "GasPriceMockOracle" ); const gasFeeOracle = await gasFeeMockFactory.deploy(); const daiEthPriceMockFactory = await ethers.getContractFactory( "DaiEthPriceMockOracle" ); const daiEthOracle = await daiEthPriceMockFactory.deploy(); const ethUsdOracleFactory = await ethers.getContractFactory( "EthUSDMockOracle" ); const ethUsdOracle = await ethUsdOracleFactory.deploy(); console.log("setting nameservice addrresses..."); await setDAOAddress("ETH_USD_ORACLE", ethUsdOracle.address); await setDAOAddress("GAS_PRICE_ORACLE", gasFeeOracle.address); await setDAOAddress("DAI_ETH_ORACLE", daiEthOracle.address); await setDAOAddress("RESERVE", goodReserve.address); await setDAOAddress("MARKET_MAKER", marketMaker.address); await setDAOAddress("REPUTATION", reputation.address); console.log("setting reserve token..."); await cDAI["mint(address,uint256)"]( goodReserve.address, 10000 ); await setReserveToken( cDAI.address, "100", //1gd "10000", //0.0001 cDai "1000000" //100% rr ); console.log("deploying compound voting..."); const votingMachine = (await upgrades.deployProxy( await ethers.getContractFactory("CompoundVotingMachine"), [nameService.address, 5760, root.address, reputation.address], { kind: "uups" } )) as unknown as CompoundVotingMachine; return { daoCreator, controller, reserve: goodReserve, avatar: await Avatar.address, gd: await Avatar.nativeToken(), identity: Identity.address, identityDeployed: Identity, nameService, setDAOAddress, runAsAvatarOnly, setSchemes, setReserveToken, genericCall, addWhitelisted, marketMaker, feeFormula: FeeFormula, daiAddress: dai.address, cdaiAddress: cDAI.address, COMP, reputation: reputation.address, votingMachine, sfContracts }; }; export const deployUBI = async (deployedDAO, withFirstClaim = true) => { let { nameService, setSchemes, genericCall, setDAOAddress } = deployedDAO; const fcFactory = new ethers.ContractFactory( FirstClaimPool.abi, FirstClaimPool.bytecode, (await ethers.getSigners())[0] ); console.log("deploying first claim...", { avatar: await nameService.getAddress("AVATAR"), identity: await nameService.getAddress("IDENTITY") }); let firstClaim = fcFactory.attach(ethers.constants.AddressZero); if (withFirstClaim) { firstClaim = await fcFactory.deploy( await nameService.getAddress("AVATAR"), await nameService.getAddress("IDENTITY"), 1000 ); } console.log("deploying ubischeme and starting...", { input: [nameService.address, firstClaim.address, 14] }); let ubiScheme = await upgrades.deployProxy( await ethers.getContractFactory("UBIScheme"), [nameService.address, firstClaim.address, 14], { kind: "uups" } ); const gd = await nameService.getAddress("GOODDOLLAR"); let 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", [ubiScheme.address, 1000000]); await genericCall(gd, encoded); console.log("set firstclaim,ubischeme as scheme and starting..."); await setSchemes([firstClaim.address, ubiScheme.address]); if (withFirstClaim) { encoded = firstClaim.interface.encodeFunctionData("setUBIScheme", [ ubiScheme.address ]); await genericCall(firstClaim.address, encoded); await firstClaim.start(); } await setDAOAddress("UBISCHEME", ubiScheme.address); return { firstClaim, ubiScheme }; }; export const deployOldUBI = async deployedDAO => { let { nameService, setSchemes, genericCall, setDAOAddress } = deployedDAO; const fcFactory = new ethers.ContractFactory( FirstClaimPool.abi, FirstClaimPool.bytecode, (await ethers.getSigners())[0] ); const ubiSchemeFactory = new ethers.ContractFactory( UBIScheme.abi, UBIScheme.bytecode, (await ethers.getSigners())[0] ); console.log("deploying first claim...", { avatar: await nameService.getAddress("AVATAR"), identity: await nameService.getAddress("IDENTITY") }); const firstClaim = await fcFactory.deploy( await nameService.getAddress("AVATAR"), await nameService.getAddress("IDENTITY"), 100 ); console.log("deploying ubischeme and starting...", { input: [nameService.address, firstClaim.address, 14] }); const block = await ethers.provider.getBlock("latest"); const startUBI = block.timestamp - 60 * 60 * 24 * 2; const endUBI = startUBI + 60 * 60 * 24 * 30; let ubiScheme = await ubiSchemeFactory.deploy( await nameService.getAddress("AVATAR"), await nameService.getAddress("IDENTITY"), firstClaim.address, startUBI, endUBI, 3, 1 ); const gd = await nameService.getAddress("GOODDOLLAR"); let 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", [ubiScheme.address, 1000000]); await genericCall(gd, encoded); console.log("set firstclaim,ubischeme as scheme and starting..."); await setSchemes([firstClaim.address]); await firstClaim.start(); await ubiScheme.start(); return { firstClaim, ubiScheme }; }; export async function increaseTime(seconds) { await ethers.provider.send("evm_increaseTime", [seconds]); await advanceBlocks(1); } export const advanceBlocks = async (blocks: number) => { await ethers.provider.send("hardhat_mine", ["0x" + blocks.toString(16)]); // required for bug https://github.com/sc-forks/solidity-coverage/issues/707 await ethers.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0x0"]); }; export const deployOldVoting = async dao => { try { const SchemeRegistrarF = new ethers.ContractFactory( SchemeRegistrar.abi, SchemeRegistrar.bytecode, (await ethers.getSigners())[0] ); const UpgradeSchemeF = new ethers.ContractFactory( UpgradeScheme.abi, UpgradeScheme.bytecode, (await ethers.getSigners())[0] ); const AbsoluteVoteF = new ethers.ContractFactory( AbsoluteVote.abi, AbsoluteVote.bytecode, (await ethers.getSigners())[0] ); const [absoluteVote, upgradeScheme, schemeRegistrar] = await Promise.all([ AbsoluteVoteF.deploy(), UpgradeSchemeF.deploy(), SchemeRegistrarF.deploy() ]); console.log("setting parameters"); const voteParametersHash = await absoluteVote.getParametersHash( 50, ethers.constants.AddressZero ); console.log("setting params for voting machine and schemes"); await Promise.all([ schemeRegistrar.setParameters( voteParametersHash, voteParametersHash, absoluteVote.address ), absoluteVote.setParameters(50, ethers.constants.AddressZero), upgradeScheme.setParameters(voteParametersHash, absoluteVote.address) ]); const upgradeParametersHash = await upgradeScheme.getParametersHash( voteParametersHash, absoluteVote.address ); // Deploy SchemeRegistrar const schemeRegisterParams = await schemeRegistrar.getParametersHash( voteParametersHash, voteParametersHash, absoluteVote.address ); let schemesArray; let paramsArray; let permissionArray; // Subscribe schemes schemesArray = [schemeRegistrar.address, upgradeScheme.address]; paramsArray = [schemeRegisterParams, upgradeParametersHash]; await dao.setSchemes(schemesArray, paramsArray); return { schemeRegistrar, upgradeScheme, absoluteVote }; } catch (e) { console.log("deployVote failed", e); } }; export const deployUniswap = async (comp, dai) => { let founder, staker, signers; [founder, staker, ...signers] = await ethers.getSigners(); const routerFactory = new ethers.ContractFactory( UniswapV2Router02.abi, UniswapV2Router02.bytecode, (await ethers.getSigners())[0] ); const uniswapFactory = new ethers.ContractFactory( UniswapV2Factory.abi, UniswapV2Factory.bytecode, (await ethers.getSigners())[0] ); const wethFactory = new ethers.ContractFactory( WETH9.abi, WETH9.bytecode, (await ethers.getSigners())[0] ); const weth = await wethFactory.deploy(); const factory = await uniswapFactory.deploy( ( await ethers.getSigners() )[0].address ); const router = await routerFactory.deploy(factory.address, weth.address); await factory.createPair(comp.address, weth.address); // Create comp and weth pair const compPairAddress = factory.getPair(comp.address, weth.address); await factory.createPair(dai.address, weth.address); // Create comp and dai pair const daiPairAddress = factory.getPair(dai.address, weth.address); const compPair = new Contract( compPairAddress, JSON.stringify(IUniswapV2Pair.abi), staker ).connect(founder); const daiPair = new Contract( daiPairAddress, JSON.stringify(IUniswapV2Pair.abi), staker ).connect(founder); await dai["mint(address,uint256)"]( founder.address, ethers.utils.parseEther("2000000") ); await dai["mint(address,uint256)"]( daiPair.address, ethers.utils.parseEther("2000000") ); await comp["mint(address,uint256)"]( compPair.address, ethers.utils.parseEther("200000") ); console.log("depositing eth to liquidity pools"); await weth.deposit({ value: ethers.utils.parseEther("4000") }); console.log(await weth.balanceOf(founder.address).then(_ => _.toString())); await weth.transfer(compPair.address, ethers.utils.parseEther("2000")); await weth.transfer(daiPair.address, ethers.utils.parseEther("2000")); console.log("minting liquidity pools"); await compPair.mint(founder.address); await daiPair.mint(founder.address); console.log("LP tokens minted"); return { router, factory, weth, compPairContract: compPair, daiPairContract: daiPair }; };