diff --git a/src/MoltenCampaign.sol b/src/MoltenCampaign.sol index 0e0ffc4..f034ea9 100644 --- a/src/MoltenCampaign.sol +++ b/src/MoltenCampaign.sol @@ -5,8 +5,7 @@ pragma solidity ^0.8.17; import {MToken} from "./MToken.sol"; import {IERC20Votes} from "./interfaces/IERC20Votes.sol"; -/// [XXX] Rename to MoltenElection -contract MoltenCampaignMarket { +contract MoltenElection { IERC20Votes public daoToken; // Threshold in daoToken-weis. uint256 public threshold; @@ -25,13 +24,16 @@ contract MoltenCampaignMarket { cooldownDuration = _cooldownDuration; } - // [XXX] We could further dependency-inject by having a separate libs + // 🎨 We could further dependency-inject by having a separate libs // MTokenDeployer and MoltenCampaignDeployer which addresses are passed as - // argument of the constructor (or the function?). - function makeCampaign() external returns (MoltenCampaign) { + // argument of the constructor/function. + function makeCampaign(string calldata delegateName) + external + returns (MoltenCampaign) + { MToken mToken = new MToken( string.concat("Molten ", daoToken.name()), - string.concat("m", daoToken.symbol()), // [XXX] Add campaigner (delegate) name + string.concat("m", daoToken.symbol(), "-", delegateName), address(this) ); MoltenCampaign mc = new MoltenCampaign( @@ -47,7 +49,7 @@ contract MoltenCampaignMarket { contract MoltenCampaign { // Immutable props. address public representative; - MoltenCampaignMarket public market; + MoltenElection public election; MToken public mToken; // Mutable props. @@ -55,23 +57,22 @@ contract MoltenCampaign { mapping(address => uint256) public staked; uint256 public cooldownEnd; - // 💜 dumb constructors. constructor( address _representative, - address marketAddress, + address electionAddress, address mTokenAddress ) { representative = _representative; - market = MoltenCampaignMarket(marketAddress); + election = MoltenElection(electionAddress); mToken = MToken(mTokenAddress); } function _getDaoToken() private view returns (IERC20Votes) { - return IERC20Votes(market.daoToken()); + return IERC20Votes(election.daoToken()); } function _resetCooldown() internal { - cooldownEnd = block.timestamp + market.cooldownDuration(); + cooldownEnd = block.timestamp + election.cooldownDuration(); } function stake(uint256 amount) public { diff --git a/test/MoltenCampaign.t.sol b/test/MoltenCampaign.t.sol index dd63cf2..7dc5e9e 100644 --- a/test/MoltenCampaign.t.sol +++ b/test/MoltenCampaign.t.sol @@ -2,41 +2,43 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import {MoltenCampaign, MoltenCampaignMarket} from "../src/MoltenCampaign.sol"; +import {MoltenCampaign, MoltenElection} from "../src/MoltenCampaign.sol"; import {MToken} from "../src/MToken.sol"; import {ERC20VotesMintable} from "./helpers/ERC20VotesMintable.sol"; import {ERC20VotesMintableMock} from "./helpers/ERC20VotesMintable.sol"; import {MTokenMock} from "./helpers/MTokenMock.sol"; abstract contract TestBase is Test { - uint256 public threshold; - uint128 public duration; - uint128 public cooldownDuration; - address public representative = address(0x123); - MoltenCampaign public mc; + uint256 public threshold = 1; + uint128 public duration = 1; + uint128 public cooldownDuration = 1; + address public delegate = address(0x123); + string public delegateName = "deldel"; + MoltenCampaign public campaign; ERC20VotesMintableMock public daoToken; MTokenMock public mToken; function setUp() public virtual { daoToken = new ERC20VotesMintableMock("DAO governance token", "GT"); - threshold = 1; - duration = 1; - cooldownDuration = 1; - MoltenCampaignMarket mcm = new MoltenCampaignMarket( + MoltenElection election = new MoltenElection( address(daoToken), threshold, duration, cooldownDuration ); - vm.prank(representative); + vm.prank(delegate); mToken = new MTokenMock( string.concat("Molten ", daoToken.name()), - string.concat("m", daoToken.symbol()), // [XXX] Add campaigner (delegate) name + string.concat("m", daoToken.symbol(), "-", delegateName), address(this) ); - mc = new MoltenCampaign(representative, address(mcm), address(mToken)); - mToken.transferOwnership(address(mc)); + campaign = new MoltenCampaign( + delegate, + address(election), + address(mToken) + ); + mToken.transferOwnership(address(campaign)); } function setFailTransfer() public virtual { @@ -57,47 +59,40 @@ abstract contract TestBase is Test { } contract StakeTest is TestBase { - address public staker; - address public staker2; - - function setUp() public override { - super.setUp(); - - staker = address(0x331); - staker2 = address(0x332); - } + address public staker = address(0x331); + address public staker2 = address(0x332); function testStakeUpdatesStaked() public { vm.prank(staker); - mc.stake(333); + campaign.stake(333); - assertEq(mc.staked(staker), 333); + assertEq(campaign.staked(staker), 333); } function testStakeUpdatesTotalStaked() public { vm.prank(staker); - mc.stake(333); + campaign.stake(333); vm.prank(staker2); - mc.stake(222); + campaign.stake(222); - assertEq(mc.totalStaked(), 555); + assertEq(campaign.totalStaked(), 555); } function testStakeCallsTransfer() public { vm.prank(staker); - mc.stake(333); + campaign.stake(333); (address from, address to, uint256 amount) = daoToken .transferFromCalledWith(); assertEq(from, staker); - assertEq(to, address(mc)); + assertEq(to, address(campaign)); assertEq(amount, 333); } function testStakeMintsMTokens() public { vm.prank(staker); - mc.stake(333); + campaign.stake(333); (address _sender, address to, uint256 amount) = mToken.mintCalledWith(); assertEq(_sender, mToken.owner()); @@ -107,9 +102,9 @@ contract StakeTest is TestBase { function testStakeResetsCooldown() public { vm.prank(staker); - mc.stake(333); + campaign.stake(333); - assertEq(mc.cooldownEnd(), block.timestamp + cooldownDuration); + assertEq(campaign.cooldownEnd(), block.timestamp + cooldownDuration); } } @@ -125,7 +120,7 @@ contract CantTransferStakeTest is TestBase { function testWrongStakeReverts() public { vm.prank(staker); vm.expectRevert("ERC20VMFM transferFrom"); - mc.stake(333); + campaign.stake(333); } } @@ -141,7 +136,7 @@ contract CantMintStakeTest is TestBase { function testWrongStakeReverts() public { vm.prank(staker); vm.expectRevert("MTFM mint"); - mc.stake(333); + campaign.stake(333); } } @@ -155,41 +150,41 @@ contract UnstakeTest is TestBase { // We call stake as a means to update the `totalStaked` and `staked` // states. vm.prank(staker); - mc.stake(333); + campaign.stake(333); vm.prank(staker2); - mc.stake(222); + campaign.stake(222); } function testUnstakeUpdatesStaked() public { vm.prank(staker); - mc.unstake(); - assertEq(mc.staked(staker), 0); + campaign.unstake(); + assertEq(campaign.staked(staker), 0); } function testUnstakeUpdatesTotalStaked() public { vm.prank(staker); - mc.unstake(); + campaign.unstake(); vm.prank(staker2); - mc.unstake(); + campaign.unstake(); - assertEq(mc.totalStaked(), 0); + assertEq(campaign.totalStaked(), 0); } function testUnstakeCallsTransfer() public { vm.prank(staker); - mc.unstake(); + campaign.unstake(); (address from, address to, uint256 amount) = daoToken .transferFromCalledWith(); - assertEq(from, address(mc)); + assertEq(from, address(campaign)); assertEq(to, staker); assertEq(amount, 333); } function testUnstakeBurnsMTokens() public { vm.prank(staker); - mc.unstake(); + campaign.unstake(); (address _sender, address to, uint256 amount) = mToken.burnCalledWith(); assertEq(_sender, mToken.owner()); @@ -199,23 +194,19 @@ contract UnstakeTest is TestBase { function testUnstakeResetsCooldown() public { vm.prank(staker); - mc.unstake(); + campaign.unstake(); - assertEq(mc.cooldownEnd(), block.timestamp + cooldownDuration); + assertEq(campaign.cooldownEnd(), block.timestamp + cooldownDuration); } } contract UnstakeNoStakeTest is TestBase { address public staker = address(0x331); - function setUp() public override { - super.setUp(); - } - function testUnstakeReverts() public { vm.prank(staker); vm.expectRevert("Molten: unstake 0"); - mc.unstake(); + campaign.unstake(); } } @@ -226,14 +217,14 @@ contract CantTransferUnstakeTest is TestBase { super.setUp(); vm.prank(staker); - mc.stake(333); + campaign.stake(333); setFailTransfer(); } function testWrongUnstakeReverts() public { vm.prank(staker); vm.expectRevert("ERC20VMFM transferFrom"); - mc.unstake(); + campaign.unstake(); } } @@ -243,16 +234,15 @@ contract CantBurnUnstakeTest is TestBase { function setUp() public override { super.setUp(); - unsetFailMint(); staker = address(0x331); vm.prank(staker); - mc.stake(333); // Set up MoltenCampaign state. + campaign.stake(333); // Set up MoltenCampaign state. setFailMint(); } function testWrongUntakeReverts() public { vm.prank(staker); vm.expectRevert("MTFM burn"); - mc.unstake(); + campaign.unstake(); } } diff --git a/test/MoltenCampaignMarket.t.sol b/test/MoltenCampaignMarket.t.sol index b5a1103..2671796 100644 --- a/test/MoltenCampaignMarket.t.sol +++ b/test/MoltenCampaignMarket.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import {ERC20VotesMintable} from "./helpers/ERC20VotesMintable.sol"; -import {MoltenCampaignMarket, MoltenCampaign} from "../src/MoltenCampaign.sol"; +import {MoltenElection, MoltenCampaign} from "../src/MoltenCampaign.sol"; contract CreationTest is Test { ERC20VotesMintable public daoToken; @@ -14,94 +14,89 @@ contract CreationTest is Test { } function testHasDaoToken() public { - MoltenCampaignMarket mcm = new MoltenCampaignMarket( + MoltenElection election = new MoltenElection( address(daoToken), 1, 1, 1 ); - assertEq(address(mcm.daoToken()), address(daoToken)); + assertEq(address(election.daoToken()), address(daoToken)); } function testHasThreshold(uint256 threshold) public { - MoltenCampaignMarket mcm = new MoltenCampaignMarket( + MoltenElection election = new MoltenElection( address(daoToken), threshold, 1, 1 ); - assertEq(mcm.threshold(), threshold); + assertEq(election.threshold(), threshold); } function testHasDuration(uint32 duration) public { - MoltenCampaignMarket mcm = new MoltenCampaignMarket( + MoltenElection election = new MoltenElection( address(daoToken), 1, duration, 1 ); - assertEq(mcm.duration(), duration); + assertEq(election.duration(), duration); } function testHasCooldownDuration(uint32 cooldownDuration) public { - MoltenCampaignMarket mcm = new MoltenCampaignMarket( + MoltenElection election = new MoltenElection( address(daoToken), 1, 1, cooldownDuration ); - assertEq(mcm.cooldownDuration(), cooldownDuration); + assertEq(election.cooldownDuration(), cooldownDuration); } } contract MakeCampaignTest is Test { - MoltenCampaignMarket public mcm; + MoltenElection public election; ERC20VotesMintable public daoToken; - uint256 public threshold; - uint32 public duration; - uint32 public cooldownDuration; function setUp() public { daoToken = new ERC20VotesMintable("DAO governance token", "GT"); - threshold = 1; - duration = 1; - cooldownDuration = 1; - mcm = new MoltenCampaignMarket( - address(daoToken), - threshold, - duration, - cooldownDuration - ); + election = new MoltenElection(address(daoToken), 1, 1, 1); } - function testHasMarket() public { - MoltenCampaign mc = mcm.makeCampaign(); + function testHasElection() public { + MoltenCampaign campaign = election.makeCampaign("butter"); - assertEq(address(mc.market()), address(mcm)); + assertEq(address(campaign.election()), address(election)); } function testHasRepresentative() public { vm.prank(address(0x123)); - MoltenCampaign mc = mcm.makeCampaign(); + MoltenCampaign campaign = election.makeCampaign("butter"); - assertEq(mc.representative(), address(0x123)); + assertEq(campaign.representative(), address(0x123)); } function testHasMToken() public { - MoltenCampaign mc = mcm.makeCampaign(); + MoltenCampaign campaign = election.makeCampaign("butter"); - assertTrue(address(mc.mToken()) != address(0x0)); + assertTrue(address(campaign.mToken()) != address(0x0)); } function testMTokenOwnerIsCampaign() public { - MoltenCampaign mc = mcm.makeCampaign(); + MoltenCampaign campaign = election.makeCampaign("butter"); // We could test this with a mock of transferOwnership but it's simple // enough here to call the actual function. - assertEq(mc.mToken().owner(), address(mc)); + assertEq(campaign.mToken().owner(), address(campaign)); + } + + function testMTokenName() public { + MoltenCampaign campaign = election.makeCampaign("butter"); + + assertEq(campaign.mToken().name(), "mGT-butter"); } }