Skip to content

Commit

Permalink
Refund the proposal fee to the proposer for accepted proposals
Browse files Browse the repository at this point in the history
  • Loading branch information
MSalman6 committed Oct 25, 2024
1 parent 78a2c3a commit 1cf4a06
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 73 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
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 1cf4a06

Please sign in to comment.