diff --git a/README.md b/README.md index c78af42..c83aa0c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ ### Requirements - foundry +- pyenv - node v16 - working rpc node (local chain) @@ -25,6 +26,12 @@ brew install libusb foundryup ``` +### Install [pyenv](https://github.com/pyenv/pyenv) +```sh +brew update +brew install pyenv +``` + ### Local Setup ```zsh diff --git a/scripts/dev-deploy.ts b/scripts/dev-deploy.ts index 6e871ef..23b93b2 100644 --- a/scripts/dev-deploy.ts +++ b/scripts/dev-deploy.ts @@ -11,165 +11,165 @@ import Passport from '../out/Passport.sol/Passport.json'; import PassportIssuer from '../out/PassportIssuer.sol/PassportIssuer.json'; const getContractFactory = (artifact: any) => { - return new ethers.ContractFactory(artifact.abi, artifact.bytecode.object, wallet); + return new ethers.ContractFactory(artifact.abi, artifact.bytecode.object, wallet); } -const deployContract = async ({ name, deployer, factory, args }: { name: string, deployer: ethers.Wallet, factory: ethers.ContractFactory, args: Array}) => { - console.log(`Deploying ${name}..`) - const contract = await factory.connect(deployer).deploy(...args); - await contract.deployed(); - console.log(`Deployed ${name} to: ${contract.address}`) - return contract; +const deployContract = async ({ name, deployer, factory, args }: { name: string, deployer: ethers.Wallet, factory: ethers.ContractFactory, args: Array }) => { + console.log(`Deploying ${name}..`) + const contract = await factory.connect(deployer).deploy(...args); + await contract.deployed(); + console.log(`Deployed ${name} to: ${contract.address}`) + return contract; } const deployNation = async () => { - const supply: BigNumber = BigNumber.from(process.env.NATION_SUPPLY ?? dec(42069, 18)); - const Factory = getContractFactory(Nation); + const supply: BigNumber = BigNumber.from(process.env.NATION_SUPPLY ?? dec(42069, 18)); + const Factory = getContractFactory(Nation); - const nationToken = await deployContract({ - name: "NATION", - deployer: wallet, - factory: Factory, - args: [] - }); + const nationToken = await deployContract({ + name: "NATION", + deployer: wallet, + factory: Factory, + args: [] + }); - await nationToken.connect(wallet).mint(wallet.address, supply); - console.log(`Minted ${formatUnits(supply, 18)} tokens to deployer address`) + await nationToken.connect(wallet).mint(wallet.address, supply); + console.log(`Minted ${formatUnits(supply, 18)} tokens to deployer address`) - return nationToken; + return nationToken; } const deployVeNation = async (nationToken: Contract) => { - const Factory = getContractFactory(VotingEscrow); + const Factory = getContractFactory(VotingEscrow); - const veNATION = await deployContract({ - name: "veNATION", - deployer: wallet, - factory: Factory, - args: [nationToken.address, "Vote-escrowed NATION", "veNATION", "veNATION_1.0.0"] - }) + const veNATION = await deployContract({ + name: "veNATION", + deployer: wallet, + factory: Factory, + args: [nationToken.address, "Vote-escrowed NATION", "veNATION", "veNATION_1.0.0"] + }) - return veNATION; + return veNATION; } const deployAirdropDistributor = async (nationToken: Contract, root: string) => { - const Factory = getContractFactory(MerkleDistributorV2); - const dropAmount = BigNumber.from(process.env.AIRDROP_AMOUNT ?? dec(314, 18)); + const Factory = getContractFactory(MerkleDistributorV2); + const dropAmount = BigNumber.from(process.env.AIRDROP_AMOUNT ?? dec(314, 18)); - const airdropDistributor = await deployContract({ - name: "nationDropContract", - deployer: wallet, - factory: Factory, - args: [] - }) - await airdropDistributor.setUp(wallet.address, nationToken.address, root); + const airdropDistributor = await deployContract({ + name: "nationDropContract", + deployer: wallet, + factory: Factory, + args: [] + }) + await airdropDistributor.setUp(wallet.address, nationToken.address, root); - await nationToken.connect(wallet).approve(airdropDistributor.address, dropAmount); - console.log(`Approved ${formatUnits(dropAmount, 18)} tokens for drop`); + await nationToken.connect(wallet).approve(airdropDistributor.address, dropAmount); + console.log(`Approved ${formatUnits(dropAmount, 18)} tokens for drop`); - return airdropDistributor; + return airdropDistributor; } const deployLiquidityDistributor = async (rewardsToken: Contract, boostToken: Contract) => { - const contractFactory = getContractFactory(LiquidityDistributor); - const tokenFactory = getContractFactory(MockERC20); - const supply = BigNumber.from(dec(314, 18)); - const rewards = BigNumber.from(dec(500, 18)); - const rewardsPeriod = 1196308; // 6 months of blocks approx - - const lpToken = await deployContract({ - name: "lpToken", - deployer: wallet, - factory: tokenFactory, - args: ["80NATION-20WETH", "80NATION-20WETH", supply] - }) - - const distributor = await deployContract({ - name: "lpRewardsContract", - deployer: wallet, - factory: contractFactory, - args: [] - }) - - await distributor.connect(wallet).initialize(rewardsToken.address, lpToken.address, boostToken.address); - - const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); - const blockNumber = await provider.getBlockNumber(); - // Setup rewards - await rewardsToken.connect(wallet).mint(distributor.address, rewards); - const startBlock = blockNumber + 5; - const endBlock = startBlock + rewardsPeriod; - - await distributor.connect(wallet).setRewards(rewards, startBlock, endBlock); - console.log(`Set ${formatUnits(rewards, 18)} NATIONs as rewards from block ${startBlock} to ${endBlock}`) - - return { "lpToken": lpToken, "lpRewardsContract": distributor} + const contractFactory = getContractFactory(LiquidityDistributor); + const tokenFactory = getContractFactory(MockERC20); + const supply = BigNumber.from(dec(314, 18)); + const rewards = BigNumber.from(dec(500, 18)); + const rewardsPeriod = 1196308; // 6 months of blocks approx + + const lpToken = await deployContract({ + name: "lpToken", + deployer: wallet, + factory: tokenFactory, + args: ["80NATION-20WETH", "80NATION-20WETH", supply] + }) + + const distributor = await deployContract({ + name: "lpRewardsContract", + deployer: wallet, + factory: contractFactory, + args: [] + }) + + await distributor.connect(wallet).initialize(rewardsToken.address, lpToken.address, boostToken.address); + + const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); + const blockNumber = await provider.getBlockNumber(); + // Setup rewards + await rewardsToken.connect(wallet).mint(distributor.address, rewards); + const startBlock = blockNumber + 5; + const endBlock = startBlock + rewardsPeriod; + + await distributor.connect(wallet).setRewards(rewards, startBlock, endBlock); + console.log(`Set ${formatUnits(rewards, 18)} NATIONs as rewards from block ${startBlock} to ${endBlock}`) + + return { "lpToken": lpToken, "lpRewardsContract": distributor } } const deployPassport = async (governanceToken: Contract) => { - const agreementStatement = "By claiming a Nation3 passport I agree to the terms defined in the following URL"; - const agreementURI = "https://bafkreiadlf3apu3u7blxw7t2yxi7oyumeuzhoasq7gqmcbaaycq342xq74.ipfs.dweb.link"; - - const passportFactory = getContractFactory(Passport); - const passportIssuerFactory = getContractFactory(PassportIssuer); - - const passportToken = await deployContract({ - name: "Passport", - deployer: wallet, - factory: passportFactory, - args: ["Nation3 Passport", "PASS3"] - }) - - const passportIssuer = await deployContract({ - name: "PassportIssuer", - deployer: wallet, - factory: passportIssuerFactory, - args: [] - }) - - await passportToken.connect(wallet).transferControl(passportIssuer.address); - // TODO: Set renderer - - await passportIssuer.connect(wallet).initialize(governanceToken.address, passportToken.address, 420); - await passportIssuer.connect(wallet).setParams(0, 0); - await passportIssuer.connect(wallet).setStatement(agreementStatement); - await passportIssuer.connect(wallet).setTermsURI(agreementURI); - await passportIssuer.connect(wallet).setEnabled(true); - - return { "passportToken": passportToken, "passportIssuer": passportIssuer, "agreementStatement": agreementStatement, "agreementURI": agreementURI } + const agreementStatement = "By claiming a Nation3 passport I agree to the terms defined in the following URL"; + const agreementURI = "https://bafkreiadlf3apu3u7blxw7t2yxi7oyumeuzhoasq7gqmcbaaycq342xq74.ipfs.dweb.link"; + + const passportFactory = getContractFactory(Passport); + const passportIssuerFactory = getContractFactory(PassportIssuer); + + const passportToken = await deployContract({ + name: "Passport", + deployer: wallet, + factory: passportFactory, + args: ["Nation3 Passport", "PASS3"] + }) + + const passportIssuer = await deployContract({ + name: "PassportIssuer", + deployer: wallet, + factory: passportIssuerFactory, + args: [] + }) + + await passportToken.connect(wallet).transferControl(passportIssuer.address); + // TODO: Set renderer + + await passportIssuer.connect(wallet).initialize(governanceToken.address, passportToken.address, 420); + await passportIssuer.connect(wallet).setParams(0, 0); + await passportIssuer.connect(wallet).setStatement(agreementStatement); + await passportIssuer.connect(wallet).setTermsURI(agreementURI); + await passportIssuer.connect(wallet).setEnabled(true); + + return { "passportToken": passportToken, "passportIssuer": passportIssuer, "agreementStatement": agreementStatement, "agreementURI": agreementURI } } const main = async () => { - console.log(`Using deployer: ${wallet.address}`); - - const NATION = await deployNation(); - const veNATION = await deployVeNation(NATION); - const lpContracts = await deployLiquidityDistributor(NATION, veNATION); - const nationDropA = await deployAirdropDistributor(NATION, "0xed145aa219b18aa3f2dc56afb2c4e0b148e429ca93b9c5f2c7a29d2101685aee"); - const nationDropB = await deployAirdropDistributor(NATION, "0xb8d662135979ae3791167c967cba4bf6fb681c665d0c03372745c483fe5089f8"); - - const passportContracts = await deployPassport(veNATION); - - const deployment = { - "nationToken": NATION.address, - "veNationToken": veNATION.address, - "balancerLPToken": lpContracts.lpToken.address, - "lpRewardsContract": lpContracts.lpRewardsContract.address, - "nationDropContracts": [nationDropA.address, nationDropB.address], - "nationPassportNFT": passportContracts.passportToken.address, - "nationPassportNFTIssuer": passportContracts.passportIssuer.address - "nationPassportAgreementStatement": passportContracts.agreementStatement, - "nationPassportAgreementURI": passportContracts.agreementURI, - } - - const manifestFile = "./deployments/local.json"; - save(deployment, manifestFile); - - console.log(`Deployment manifest saved to ${manifestFile}`) + console.log(`Using deployer: ${wallet.address}`); + + const NATION = await deployNation(); + const veNATION = await deployVeNation(NATION); + const lpContracts = await deployLiquidityDistributor(NATION, veNATION); + const nationDropA = await deployAirdropDistributor(NATION, "0xed145aa219b18aa3f2dc56afb2c4e0b148e429ca93b9c5f2c7a29d2101685aee"); + const nationDropB = await deployAirdropDistributor(NATION, "0xb8d662135979ae3791167c967cba4bf6fb681c665d0c03372745c483fe5089f8"); + + const passportContracts = await deployPassport(veNATION); + + const deployment = { + "nationToken": NATION.address, + "veNationToken": veNATION.address, + "balancerLPToken": lpContracts.lpToken.address, + "lpRewardsContract": lpContracts.lpRewardsContract.address, + "nationDropContracts": [nationDropA.address, nationDropB.address], + "nationPassportNFT": passportContracts.passportToken.address, + "nationPassportNFTIssuer": passportContracts.passportIssuer.address, + "nationPassportAgreementStatement": passportContracts.agreementStatement, + "nationPassportAgreementURI": passportContracts.agreementURI, + } + + const manifestFile = "./deployments/local.json"; + save(deployment, manifestFile); + + console.log(`Deployment manifest saved to ${manifestFile}`) } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/vyper-compile.ts b/scripts/vyper-compile.ts index e80b337..e77d556 100644 --- a/scripts/vyper-compile.ts +++ b/scripts/vyper-compile.ts @@ -2,61 +2,61 @@ import fs from "fs"; import { exec } from "child_process"; const INPUT_SCHEMA = { - "language": "Vyper", - "sources": {}, - "settings": { - "optimize": true, - "outputSelection": { - "*": ["evm.bytecode", "abi"] - } + "language": "Vyper", + "sources": {}, + "settings": { + "optimize": true, + "outputSelection": { + "*": ["evm.bytecode", "abi"] } + } } // Wrap vyper-json compile on a single script // Format JSON artifact file to be compatible with ethers const main = async () => { - const contractsBaseDir = "src"; - const outputBaseDir = "out"; + const contractsBaseDir = "src"; + const outputBaseDir = "out"; - const contractName = "VotingEscrow"; - const contractDir = "governance"; - const contractFile = "VotingEscrow.vy"; - const contractFilePath = `${contractsBaseDir}/${contractDir}/${contractFile}`; + const contractName = "VotingEscrow"; + const contractDir = "governance"; + const contractFile = "VotingEscrow.vy"; + const contractFilePath = `${contractsBaseDir}/${contractDir}/${contractFile}`; - const contractContent = fs.readFileSync(`${process.cwd()}/${contractFilePath}`, { encoding: "utf8" }); - console.log(contractFilePath) + const contractContent = fs.readFileSync(`${process.cwd()}/${contractFilePath}`, { encoding: "utf8" }); + console.log(contractFilePath) - let inputParams = JSON.parse(JSON.stringify(INPUT_SCHEMA)); - inputParams.sources[contractFilePath] = {"content": contractContent}; + let inputParams = JSON.parse(JSON.stringify(INPUT_SCHEMA)); + inputParams.sources[contractFilePath] = { "content": contractContent }; - fs.writeFileSync("temp.json", JSON.stringify(inputParams, null, 1)); + fs.writeFileSync("temp.json", JSON.stringify(inputParams, null, 1)); - const outputDir = `${outputBaseDir}/${contractFile}`; - if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true}); + const outputDir = `${outputBaseDir}/${contractFile}`; + if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true }); - exec( - ["vyper-json", "temp.json"].join(" "), - (error, stdout, stderr) => { - const artifacts = JSON.parse(stdout); - const contractArtifact = artifacts.contracts[contractFilePath][contractName]; - const outputFile = `${outputDir}/${contractName}.json`; - const outputArtifact: {abi: Array, bytecode: Object} = { "abi": [], "bytecode": {}}; + exec( + ["pyenv exec vyper-json", "temp.json"].join(" "), + (error, stdout, stderr) => { + const artifacts = JSON.parse(stdout); + const contractArtifact = artifacts.contracts[contractFilePath][contractName]; + const outputFile = `${outputDir}/${contractName}.json`; + const outputArtifact: { abi: Array, bytecode: Object } = { "abi": [], "bytecode": {} }; - outputArtifact.bytecode = contractArtifact.evm.bytecode; - contractArtifact.abi.map((item: any) => { - delete item.gas; - outputArtifact.abi.push(item); - }); + outputArtifact.bytecode = contractArtifact.evm.bytecode; + contractArtifact.abi.map((item: any) => { + delete item.gas; + outputArtifact.abi.push(item); + }); - fs.writeFileSync(outputFile, JSON.stringify(outputArtifact, null, 1)); - fs.unlinkSync("temp.json"); - } - ); + fs.writeFileSync(outputFile, JSON.stringify(outputArtifact, null, 1)); + fs.unlinkSync("temp.json"); + } + ); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; });