UNPKG

@vechain/vebetterdao-contracts

Version:

Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.

216 lines (215 loc) 10.5 kB
import { mintStargateNFTs } from "../helpers"; import { airdropB3trFromTreasury, airdropVTHO } from "../helpers/airdrop"; import { bootstrapEmissions, startEmissions } from "../helpers/emissions"; import { getSeedAccounts, getTestKeys, SeedStrategy } from "../helpers/seedAccounts"; import { convertB3trForVot3 } from "../helpers/swap"; import { ethers } from "hardhat"; import { Address } from "@vechain/sdk-core"; import { assignAppCategories, endorseXApps, registerXDapps } from "../helpers/xApp"; const accounts = getTestKeys(17); const xDappCreatorAccounts = accounts.slice(0, 8); export const APPS = [ { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Mugshot", metadataURI: "bafkreiba646cx2y2pv7vrj4sd4fw7m4kdebdthkum2p6jczs5umid5cjgy", categories: ["nutrition", "plastic-waste-recycling"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Cleanify", metadataURI: "bafkreigefkdis3kxspa5ovz4wybcbebecrqw256m7yimwj2jmrmsx57fui", categories: ["plastic-waste-recycling"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "GreenCart", metadataURI: "bafkreigu2rw4psbpazeudqhcpjdxsrhrtz3unckcnlidsjsiqjsgje3kli", categories: ["sustainable-shopping"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Green Ambassador Challenge", metadataURI: "bafkreibjxezzffohborgmtqsp6w7koxxdc55bd4dsiud4dlfp2vno5f4le", categories: ["education-learning", "green-mobility-travel"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Oily", metadataURI: "bafkreiakrtehu5xmzrxiwnwo2l6yfgt7ejhjhwxtgkn57vnflpfklx7ioe", categories: ["renewable-energy-efficiency"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "EVearn", metadataURI: "bafkreiajy2ldq3o3rhw2gmzmvqbqego76oh2gtmkcz3fxsmfcre6jzuzfu", categories: ["green-finance-defi", "renewable-energy-efficiency"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Vyvo", metadataURI: "bafkreib6uklcdfpudaqlu4budf7f2ngays2rrhqcxshqnjvme5umtykiui", categories: ["fitness-wellness"], }, { admin: accounts[6].address.toString(), teamWalletAddress: accounts[6].address.toString(), name: "Non Fungible Book Club (NFBC)", metadataURI: "bafkreieyqnioauddqzpscgo3gm5nlxu6r3sm2zriz4ft23rw2efc6e252q", categories: ["education-learning"], }, ]; export const setupEnvironment = async (config, emissions, treasury, x2EarnApps, _governor, _xAllocationVoting, b3tr, vot3, stargateMock) => { switch (config) { case "local": case "testnet": case "testnet-staging": await setupLocalEnvironment(config, emissions, treasury, x2EarnApps, b3tr, vot3, stargateMock); break; case "mainnet": await setupMainnetEnvironment(emissions, x2EarnApps); break; default: throw new Error(`Unsupported app environment: ${config}`); } }; export const updateGMMultipliers = async (levels, multipliers, voterRewards) => { for (let i = 0; i < levels.length; i++) { const level = levels[i]; const multiplier = multipliers[i]; // Update the multiplier for the level await voterRewards.setLevelToMultiplierNow(level, multiplier); } }; export const endorseAndVerifySeededApps = async (x2EarnApps, stargateMock, endorserSigners) => { const mintAccounts = []; const mintLevels = []; for (const acct of endorserSigners) { mintAccounts.push(acct, acct, acct); mintLevels.push(7, 7, 7); } await mintStargateNFTs(stargateMock, mintAccounts, mintLevels); const unendorsedApps = await x2EarnApps.unendorsedAppIds(); await endorseXApps(endorserSigners, x2EarnApps, unendorsedApps, stargateMock); await verifyEndorsedApps(x2EarnApps); }; const verifyEndorsedApps = async (x2EarnApps) => { const remaining = await x2EarnApps.unendorsedAppIds(); console.log(`Remaining apps: ${remaining.length} ${remaining.join(", ")}`); if (remaining.length > 0) { const threshold = Number(await x2EarnApps.endorsementScoreThreshold()); console.log(`Threshold: ${threshold}`); for (const appId of remaining) { const score = Number(await x2EarnApps.getScore(appId)); console.error(`App ${appId} has ${score}/${threshold} endorsement points`); } throw new Error(`${remaining.length} app(s) did not reach endorsement threshold`); } console.log(`All apps verified as endorsed (>= ${await x2EarnApps.endorsementScoreThreshold()} pts)`); }; export const setupLocalEnvironment = async (env, emissions, treasury, x2EarnApps, b3tr, vot3, stargateMock) => { const start = performance.now(); // Define specific accounts const admin = accounts[0]; // Make sure the first 10 accounts have a VTHO balance await airdropVTHO(accounts.slice(1, 10).map(acct => acct.address), 5000n, admin); // Bootstrap emissions const emissionsContract = await emissions.getAddress(); await bootstrapEmissions(emissionsContract, admin); // Add x-apps to the XAllocationPool const x2EarnAppsAddress = await x2EarnApps.getAddress(); await registerXDapps(x2EarnAppsAddress, xDappCreatorAccounts, APPS); // Assign categories to apps (deployer has DEFAULT_ADMIN_ROLE) const deployer = (await ethers.getSigners())[0]; await assignAppCategories(x2EarnApps, deployer, APPS); // Seed the first 10 accounts with B3TR + VOT3 const treasuryAddress = await treasury.getAddress(); const allAccounts = getSeedAccounts(SeedStrategy.FIXED, 10 + APPS.length, 0); const first10 = allAccounts.slice(0, 10); const appCreatorSeedAccounts = allAccounts.slice(10); await airdropVTHO(first10.map(acct => acct.key.address), 500n, admin); // Step 1: small airdrop (10k B3TR) → convert all to VOT3 const vot3Accounts = first10.map(a => ({ ...a, amount: ethers.parseEther("10000") })); await airdropB3trFromTreasury(treasuryAddress, admin, [...vot3Accounts, ...appCreatorSeedAccounts]); await convertB3trForVot3(b3tr, vot3, vot3Accounts); // Step 2: airdrop 50k B3TR — stays as B3TR for challenges etc. const b3trAccounts = first10.map(a => ({ ...a, amount: ethers.parseEther("50000") })); await airdropB3trFromTreasury(treasuryAddress, admin, b3trAccounts); // If the first 8 accounts does not have the correct nodes, run the following line await startEmissions(emissionsContract, admin); // Endorsement strategy: on solo (local) we can mint mock Stargate NFTs because accounts // are pre-funded with VET. On real networks (testnet/testnet-staging) accounts lack VET, // so we set endorsement threshold to 0 and call checkEndorsement to auto-endorse. if (env === "local") { const allSigners = await ethers.getSigners(); const endorserSigners = allSigners.slice(0, 10); await airdropVTHO(endorserSigners.map(acct => Address.of(acct.address)), 5000n, admin); // Every endorser gets 3 MjolnirX nodes (need 3 per app since max 49 pts/node/app) // Total: 30 nodes x 100 pts = 3000 pts >> 8 apps x 100 threshold await endorseAndVerifySeededApps(x2EarnApps, stargateMock, endorserSigners); } else { // testnet and testnet-staging: set threshold to 0 so apps auto-endorse via checkEndorsement await x2EarnApps .connect(deployer) .updateEndorsementScoreThreshold(0) .then(async (tx) => await tx.wait()); console.log("Endorsement score threshold set to 0"); const unendorsedApps = await x2EarnApps.unendorsedAppIds(); for (const appId of unendorsedApps) { const tx = await x2EarnApps.checkEndorsement(appId); await tx.wait(); } console.log(`checkEndorsement called for ${unendorsedApps.length} apps`); await verifyEndorsedApps(x2EarnApps); } const end = new Date(performance.now() - start); console.log(`Setup complete in ${end.getMinutes()}m ${end.getSeconds()}s`); }; export const setupMainnetEnvironment = async (emissions, x2EarnApps) => { console.log("================ Setup Mainnet environment"); const start = performance.now(); const mainnet_admin_addresses = new Map([ ["Mugshot", "0xbfe2122a82c0aea091514f57c7713c3118101eda"], ["Cleanify", "0x6b020e5c8e8574388a275cc498b27e3eb91ec3f2"], ["GreenCart", "0x4e506ee842ba8ccce88e424522506f5b860e5c9b"], ["Green Ambassador Challenge", "0x15e74aeb00d367a5a20c61b469df30a25f0e602f"], ["Oily", "0xd52e3356231c9fa86bb9fab731f8c0c3f1018753"], ["EVearn", "0xb2919e12d035a484f8414643b606b2a180224f54"], ["Vyvo", "0x61ffc950b04090f5ce857ebf056852a6d27b0c3c"], ["Non Fungible Book Club (NFBC)", "0xbe50d2fae95b23082f351e290548365e84ec1780"], ]); const admin = accounts[0]; // Bootstrap emissions const emissionsContract = await emissions.getAddress(); await bootstrapEmissions(emissionsContract, admin); // Add x-apps to the XAllocationPool console.log("Adding x-apps..."); // Add x-apps to the XAllocationPool const x2EarnAppsAddress = await x2EarnApps.getAddress(); // Overwrite the admin and teamWalletAddress with the mainnet addresses APPS.forEach(app => { const newAddress = mainnet_admin_addresses.get(app.name); if (newAddress) { app.admin = newAddress; app.teamWalletAddress = newAddress; } else { throw new Error(`Mainnet admin address not found for ${app.name}`); } console.log(app.name); console.log("Admin: ", app.admin); console.log("Team Wallet Address: ", app.teamWalletAddress); }); await registerXDapps(x2EarnAppsAddress, xDappCreatorAccounts, APPS); console.log("x-apps added"); const end = performance.now(); console.log(`Setup complete in ${end - start}ms`); };