Skip to content

Commit

Permalink
Merge pull request DMDcoin#39 from MSalman6/alpha4
Browse files Browse the repository at this point in the history
Refund accepted proposal fees to the proposer
  • Loading branch information
SurfingNerd authored Oct 26, 2024
2 parents 822b2c8 + 1cf4a06 commit 62c064f
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 74 deletions.
11 changes: 8 additions & 3 deletions contracts/DiamondDao.sol
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,14 @@ contract DiamondDao is IDiamondDao, Initializable, ReentrancyGuardUpgradeable, V
proposal.description = description;
proposal.discussionUrl = discussionUrl;
proposal.daoPhaseCount = daoPhaseCount;
proposal.proposalFee = createProposalFee;
proposal.proposalType = proposalType;

currentPhaseProposals.push(proposalId);
statistic.total += 1;
unfinalizedProposals += 1;

_transfer(reinsertPot, msg.value);

emit ProposalCreated(proposer, proposalId, targets, values, calldatas, title, description, discussionUrl);
emit ProposalCreated(proposer, proposalId, targets, values, calldatas, title, description, discussionUrl, createProposalFee);
}

function cancel(uint256 proposalId, string calldata reason) external exists(proposalId) {
Expand Down Expand Up @@ -343,8 +342,14 @@ contract DiamondDao is IDiamondDao, Initializable, ReentrancyGuardUpgradeable, V

if (accepted) {
statistic.accepted += 1;

// return fee back to the proposer
_transfer(proposal.proposer, proposal.proposalFee);
} else {
statistic.declined += 1;

// send fee to the reinsert pot
_transfer(reinsertPot, proposal.proposalFee);
}

unfinalizedProposals -= 1;
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IDiamondDao.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ interface IDiamondDao {
bytes[] calldatas,
string title,
string description,
string discussionUrl
string discussionUrl,
uint256 proposalFee
);

event ProposalCanceled(address indexed proposer, uint256 indexed proposalId, string reason);
Expand Down
1 change: 1 addition & 0 deletions contracts/library/DaoStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct Proposal {
string description;
string discussionUrl;
uint256 daoPhaseCount;
uint256 proposalFee;
ProposalType proposalType;
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"clean": "rm -rf artifacts/ cache/ coverage/ typechain/ typechain-types/ coverage.json",
"coverage": "npx hardhat coverage --show-stack-traces --config ./hardhat-coverage.config.ts --solcoverjs ./.solcover.js",
"update-core": "rm -rf node_modules/diamond-contracts-core && npm install diamond-contracts-core --save --legacy-peer-deps",
"deployForDaoUpgrade": "npx hardhat run scripts/deployForDaoUpgrade.ts"
"deployForDaoUpgrade": "npx hardhat run scripts/deployForDaoUpgrade.ts",
"clean-install": "rm -f package-lock.json && rm -rf node_modules/diamond-contracts-core && npm install"
},
"repository": {
"type": "git",
Expand Down
29 changes: 29 additions & 0 deletions scripts/getUpgradeCallData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import hre from "hardhat";
import { ethers, upgrades } from "hardhat";
import { attachProxyAdminV5, } from "@openzeppelin/hardhat-upgrades/dist/utils";

const daoAddress = "0xDA0da0da0Da0Da0Da0DA00DA0da0da0DA0DA0dA0";

async function getUpgradeCalldata() {
const proxyAdmin = await attachProxyAdminV5(
hre,
await upgrades.erc1967.getAdminAddress(daoAddress)
);

console.log("Proxy Admin: ", proxyAdmin.target)
const factory = await ethers.getContractFactory("DiamondDao");
const newImplementation = await upgrades.deployImplementation(factory);

console.log("New imp. address: ", newImplementation)
const calldata = proxyAdmin.interface.encodeFunctionData("upgradeAndCall", [
daoAddress,
newImplementation,
ethers.hexlify(new Uint8Array()),
]);
console.log("Calldata: ", calldata);
}

getUpgradeCalldata().catch((error) => {
console.error(error);
process.exitCode = 1;
});
123 changes: 59 additions & 64 deletions test/DiamondDao.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,55 +447,16 @@ describe("DiamondDao contract", function () {
.withArgs(DaoPhase.Voting);
});

it("should revert propose if fee transfer failed", async function () {
const daoFactory = await ethers.getContractFactory("DiamondDao");
const mockFactory = await ethers.getContractFactory("MockValidatorSetHbbft");

const mockValidatorSet = await mockFactory.deploy();
await mockValidatorSet.waitForDeployment();

const startTime = await time.latest();

const daoProxy = await upgrades.deployProxy(daoFactory, [
await mockValidatorSet.getAddress(),
await mockValidatorSet.getAddress(),
await mockValidatorSet.getAddress(),
ethers.ZeroAddress,
createProposalFee,
startTime + 1
], {
initializer: "initialize",
});

await daoProxy.waitForDeployment();

const dao = daoFactory.attach(await daoProxy.getAddress()) as DiamondDao;

const targets = [users[3].address];
const values = [ethers.parseEther("1")];
const calldatas = [EmptyBytes];
const description = "test";

await expect(
dao.propose(
targets,
values,
calldatas,
"title",
description,
"url",
{ value: createProposalFee }
)
).to.be.revertedWithCustomError(dao, "TransferFailed")
.withArgs(await dao.getAddress(), await mockValidatorSet.getAddress(), createProposalFee);
});

it("should revert propose if limit was reached", async function () {
const proposer = users[2];
const { dao } = await loadFixture(deployFixture);

for (let i = 0; i < 1000; ++i) {
expect(await createProposal(dao, users[1], `proposal ${i}`));
const usersSubset = users.slice(10, 20);

for (let i = 0; i < 100; ++i) {
for (const user of usersSubset) {
expect(await createProposal(dao, user, `proposal ${i} ${user.address}`));
}
}

await expect(
Expand Down Expand Up @@ -531,24 +492,6 @@ describe("DiamondDao contract", function () {
).to.be.revertedWithCustomError(dao, "UnfinalizedProposalsExist");
});

it("should create proposal and transfer fee to reinsert pot", async function () {
const { dao } = await loadFixture(deployFixture);

const proposer = users[2];

const targets = [users[3].address];
const values = [ethers.parseEther("1")];
const calldatas = [EmptyBytes];
const description = "test";

await expect(
dao.connect(proposer).propose(targets, values, calldatas, "title", description, "url", { value: createProposalFee })
).to.changeEtherBalances(
[proposer.address, reinsertPot.address],
[-createProposalFee, createProposalFee]
);
});

it("should create proposal and emit event", async function () {
const { dao } = await loadFixture(deployFixture);

Expand Down Expand Up @@ -577,7 +520,8 @@ describe("DiamondDao contract", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);
});

Expand Down Expand Up @@ -623,6 +567,7 @@ describe("DiamondDao contract", function () {
description,
"url",
1, // first phase
createProposalFee,
0 // open proposal
]);
});
Expand Down Expand Up @@ -1191,6 +1136,31 @@ describe("DiamondDao contract", function () {
expect(statisticsAfter.accepted).to.equal(statisticBefore.accepted + 1n);
});

it("should finalize accepted proposal and transfer fee to back to proposer", async function () {
const { dao, mockValidatorSet, mockStaking } = await loadFixture(deployFixture);

const proposer = users[2];
const voters = users.slice(10, 20);

const { proposalId } = await createProposal(dao, proposer, "a");

await addValidatorsStake(mockValidatorSet, mockStaking, voters);

await swithPhase(dao);

await vote(dao, proposalId, voters.slice(0, 10), Vote.Yes);
await vote(dao, proposalId, voters.slice(10), Vote.No);

await swithPhase(dao);

await expect(
await dao.finalize(proposalId)
).to.changeEtherBalances(
[await dao.getAddress(), proposer.address],
[-createProposalFee, createProposalFee]
);
});

it("should finalize declined proposal and emit event", async function () {
const { dao, mockValidatorSet, mockStaking } = await loadFixture(deployFixture);

Expand All @@ -1216,6 +1186,31 @@ describe("DiamondDao contract", function () {
expect((await dao.getProposal(proposalId)).state).to.equal(ProposalState.Declined);
});

it("should finalize declined proposal and transfer fee to reinsert pot", async function () {
const { dao, mockValidatorSet, mockStaking } = await loadFixture(deployFixture);

const proposer = users[2];
const voters = users.slice(10, 20);

const { proposalId } = await createProposal(dao, proposer, "a");

await addValidatorsStake(mockValidatorSet, mockStaking, voters);

await swithPhase(dao);

await vote(dao, proposalId, voters.slice(0, 10), Vote.No);
await vote(dao, proposalId, voters.slice(10), Vote.Yes);

await swithPhase(dao);

await expect(
await dao.finalize(proposalId)
).to.changeEtherBalances(
[await dao.getAddress(), reinsertPot.address],
[-createProposalFee, createProposalFee]
);
});

it("should finalize declined proposal and update statistics", async function () {
const voters = users.slice(10, 20);

Expand Down
57 changes: 57 additions & 0 deletions test/ProposalExecution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,63 @@ describe("DAO proposal execution", function () {

expect(await dao.createProposalFee()).to.equal(newFeeValue);
});

it("should update createProposalFee and refund original fee to proposers", async function () {
const firstProposer = users[2];
const secondProposer = users[3];
const voters = users.slice(5, 15);

const { dao, mockValidatorSet, mockStaking } = await loadFixture(deployFixture);

const originalFeeValue = createProposalFee;
const newFeeValue = ethers.parseEther('20');
const calldata = dao.interface.encodeFunctionData("setCreateProposalFee", [newFeeValue]);

const { proposalId: firstProposalId } = await createProposal(
dao,
firstProposer,
getRandomBigInt().toString(),
[await dao.getAddress()],
[0n],
[calldata]
);

const { proposalId: secondProposalId } = await createProposal(
dao,
secondProposer,
getRandomBigInt().toString(),
[await dao.getAddress()],
[0n],
[calldata]
);

await addValidatorsStake(mockValidatorSet, mockStaking, voters);
await swithPhase(dao);
await vote(dao, firstProposalId, voters, Vote.Yes);
await vote(dao, secondProposalId, voters, Vote.Yes);
await swithPhase(dao);

await expect(
await dao.finalize(firstProposalId)
).to.changeEtherBalances(
[await dao.getAddress(), firstProposer.address],
[-originalFeeValue, originalFeeValue]
);

await expect(dao.connect(firstProposer).execute(firstProposalId))
.to.emit(dao, "SetCreateProposalFee")
.withArgs(newFeeValue);

expect(await dao.createProposalFee()).to.equal(newFeeValue);

// even after fee is changed the user should get his original fee back\
await expect(
await dao.finalize(secondProposalId)
).to.changeEtherBalances(
[await dao.getAddress(), secondProposer.address],
[-originalFeeValue, originalFeeValue]
);
});
});

describe("self upgrade", async function () {
Expand Down
6 changes: 4 additions & 2 deletions test/ProposalFinalization.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ describe("Proposal Acceptance Threshold", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);

await swithPhase(dao);
Expand Down Expand Up @@ -508,7 +509,8 @@ describe("Proposal Acceptance Threshold", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);

await swithPhase(dao);
Expand Down
9 changes: 6 additions & 3 deletions test/ProposalValueGuards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ describe("DAO Ecosystem Paramater Change Value Guards Test", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);
});

Expand Down Expand Up @@ -306,7 +307,8 @@ describe("DAO Ecosystem Paramater Change Value Guards Test", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);

expect((await dao.getProposal(proposalId)).proposalType).to.equal(2);
Expand Down Expand Up @@ -339,7 +341,8 @@ describe("DAO Ecosystem Paramater Change Value Guards Test", function () {
calldatas,
"title",
description,
"url"
"url",
createProposalFee
);

expect((await dao.getProposal(proposalId)).proposalType).to.equal(1);
Expand Down

0 comments on commit 62c064f

Please sign in to comment.