diff --git a/contracts/InternalMarket/InternalMarket.sol b/contracts/InternalMarket/InternalMarket.sol index 9a5bb9e..8ecff95 100644 --- a/contracts/InternalMarket/InternalMarket.sol +++ b/contracts/InternalMarket/InternalMarket.sol @@ -50,6 +50,13 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { * @param amount The amount of tokens to offer for sale. */ function makeOffer(uint256 amount) public virtual { + require( + _shareholderRegistry.isAtLeast( + _shareholderRegistry.CONTRIBUTOR_STATUS(), + msg.sender + ), + "InternalMarket: only contributors can make offers" + ); _makeOffer(_msgSender(), amount); } diff --git a/hardhat.config.ts b/hardhat.config.ts index 9a384ac..0b8a198 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -74,7 +74,7 @@ const config: HardhatUserConfig = { accounts: [POLYGON_PRIVATE_KEY], }, tevmos: { - url: "https://eth.bd.evmos.dev:8545", + url: "https://evmos-testnet.lava.build", accounts: [TEVMOS_PRIVATE_KEY], }, evmos: { diff --git a/tasks/deploy.ts b/tasks/deploy.ts index 09310d7..dbbbcde 100644 --- a/tasks/deploy.ts +++ b/tasks/deploy.ts @@ -15,11 +15,12 @@ import { question } from "../lib/utils"; const MULTISIG_MAINNET = "0xd232121c41EF9ad4e4d0251BdCbe60b9F3D20758"; const MULTISIG_TESTNET = "0x7549fe2ED3c16240f97FE736146347409C6dD81D"; -task("deploy", "Deploy DAO") +task("deploy:dao", "Deploy DAO") .addFlag("verify", "Verify contracts") .addFlag("restart", "Start a new deployment from scratch") .setAction( async ({ verify, restart }: { verify: boolean; restart: boolean }, hre) => { + if (restart) await hre.run("compile", { force: true }); const neokingdom = await NeokingdomDAOHardhat.initialize(hre, { verifyContracts: verify, verbose: true, @@ -30,7 +31,7 @@ task("deploy", "Deploy DAO") } ); -task("setup", "Set up the DAO") +task("setup:dao", "Set up the DAO") .addFlag("mainnet", "Go to Mainnet") .setAction(async ({ mainnet }: { mainnet: boolean }, hre) => { let sequence = SETUP_SEQUENCE_TESTNET; diff --git a/tasks/upgrade.ts b/tasks/upgrade.ts index ca0f49d..5481bdd 100644 --- a/tasks/upgrade.ts +++ b/tasks/upgrade.ts @@ -1,45 +1,43 @@ import { task } from "hardhat/config"; - -import { - GovernanceToken, - GovernanceToken__factory, - InternalMarket__factory, - ProxyAdmin, - ProxyAdmin__factory, - ResolutionManager__factory, -} from "../typechain"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { exit } from "process"; import { NeokingdomDAOHardhat } from "../lib"; +import { NeokingdomContracts } from "../lib/internal/types"; import { question } from "../lib/utils"; -task("upgrade:resolution", "Upgrade ResolutionManager", async (_, hre) => { - const resolutionFactory = (await hre.ethers.getContractFactory( - "ResolutionManager" - )) as ResolutionManager__factory; - +function toPascalCase(str: string): string { + let words = str.split(/(?=[A-Z])/); + + // Capitalize the first letter of each word, leave the rest as it is + let pascalCase = words + .map((word) => word[0].toUpperCase() + word.slice(1)) + .join(""); + + return pascalCase; +} + +const validContracts = [ + "governanceToken", + "internalMarket", + "redemptionController", + "resolutionManager", + "shareholderRegistry", + "voting", +]; + +async function upgrade( + hre: HardhatRuntimeEnvironment, + contractType: keyof NeokingdomContracts +) { + await hre.run("compile", { force: true }); const neokingdom = await NeokingdomDAOHardhat.initialize(hre); const contracts = await neokingdom.loadContracts(); - console.log("Upgrade ResolutionManager"); - console.log(" Network:", hre.network.name); - - const resolutionContract = await hre.upgrades.upgradeProxy( - contracts.resolutionManager.address, - resolutionFactory - ); - await resolutionContract.deployed(); - - console.log(" Address:", resolutionContract.address); - console.log("Resolution upgraded"); -}); - -task("upgrade:market", "Upgrade Internal Market", async (_, hre) => { - const internalMarketFactory = (await hre.ethers.getContractFactory( - "InternalMarket" - )) as InternalMarket__factory; - const neokingdom = await NeokingdomDAOHardhat.initialize(hre); - const contracts = await neokingdom.loadContracts(); - console.log(`Upgrade InternalMarket ${contracts.internalMarket.address}`); + const contractTypeStr = toPascalCase(contractType); + const contractFactory = await hre.ethers.getContractFactory(contractTypeStr); + const proxyAddress = contracts[contractType].address; + console.log(`Upgrade ${contractTypeStr} ${proxyAddress}`); console.log(" Network:", hre.network.name); const answer = await question( @@ -47,59 +45,25 @@ task("upgrade:market", "Upgrade Internal Market", async (_, hre) => { ); if (answer == "GO") { - const internalMarketContract = await hre.upgrades.upgradeProxy( - contracts.internalMarket.address, - internalMarketFactory + const contractInstance = await hre.upgrades.upgradeProxy( + proxyAddress, + contractFactory ); - await internalMarketContract.deployed(); + await contractInstance.deployed(); - console.log(" Address:", internalMarketContract.address); - console.log("InternalMarket upgraded"); + console.log(" Address:", contractInstance.address); + console.log(`${contractTypeStr} upgraded`); } -}); - -task("upgrade:governance", "Upgrade Governance Token", async (_, hre) => { - const governanceTokenFactory = (await hre.ethers.getContractFactory( - "GovernanceToken" - )) as GovernanceToken__factory; - - const neokingdom = await NeokingdomDAOHardhat.initialize(hre); - const contracts = await neokingdom.loadContracts(); - console.log(`Upgrade GovernanceToken ${contracts.governanceToken.address}`); - console.log(" Network:", hre.network.name); - - const answer = await question( - "This action is irreversible. Please type 'GO' to continue.\n" - ); - - if (answer == "GO") { - const governanceTokenContract = (await hre.upgrades.upgradeProxy( - contracts.governanceToken.address, - governanceTokenFactory - )) as GovernanceToken; - await governanceTokenContract.deployed(); - - console.log(" Address:", governanceTokenContract.address); - console.log("GovernanceToken upgraded"); - } -}); - -task("impl", "Get Proxy Impl") - .addParam("admin", "Proxy Admin") - .addParam("address", "Proxy address") - .setAction( - async ({ admin, address }: { admin: string; address: string }, hre) => { - const [deployer] = await hre.ethers.getSigners(); - const ProxyAdmin = await hre.ethers.getContractFactory("ProxyAdmin"); - const proxyAdmin = ProxyAdmin.attach(admin).connect( - deployer - ) as ProxyAdmin; - - console.log(" Proxy Owner:", await proxyAdmin.owner()); - - console.log( - " Address:", - await proxyAdmin.getProxyImplementation(address) - ); +} + +task("upgrade") + .addPositionalParam("contract", "The smart contract to upgrade") + .setAction(async ({ contract }: { contract: string }, hre) => { + console.log(contract); + if (!validContracts.includes(contract)) { + console.error(`Invalid contract. Valid options are: ${validContracts}`); + exit(1); } - ); + + await upgrade(hre, contract as keyof NeokingdomContracts); + }); diff --git a/test/Integration.ts b/test/Integration.ts index 2960394..e50b1f2 100644 --- a/test/Integration.ts +++ b/test/Integration.ts @@ -679,6 +679,11 @@ describe("Integration", async () => { "Resolution: account cannot vote" ); + // user3 cannot offer tokens + await expect(internalMarket.connect(user3).makeOffer(10)).revertedWith( + "InternalMarket: only contributors can make offers" + ); + // ... and finally the world is saved. expect(await resolutionManager.getResolutionResult(resolutionId)).to.be .true; diff --git a/test/InternalMarket.ts b/test/InternalMarket.ts index 2e9f669..16e6b37 100644 --- a/test/InternalMarket.ts +++ b/test/InternalMarket.ts @@ -202,6 +202,13 @@ describe("InternalMarket", async () => { ); }); + it("should revert when called by a non-contributor", async () => { + registry.isAtLeast.returns(false); + await expect(internalMarket.makeOffer(1000)).revertedWith( + "InternalMarket: only contributors can make offers" + ); + }); + describe("redeem", async () => { describe("with some unlocked tokens", async () => { beforeEach(async () => { diff --git a/test/RedemptionController.ts b/test/RedemptionController.ts index 6957209..0a1d47c 100644 --- a/test/RedemptionController.ts +++ b/test/RedemptionController.ts @@ -202,7 +202,7 @@ describe("RedemptionController", () => { ); }); - describe.only("when 10 tokens are redeemable", async () => { + describe("when 10 tokens are redeemable", async () => { beforeEach(async () => { await redemptionController.afterMint(account.address, 10); await redemptionController.afterOffer(account.address, 10);