Skip to content

Commit 65be9c1

Browse files
author
Mike
committed
merge main
2 parents 0a7edb0 + 545ec42 commit 65be9c1

File tree

8 files changed

+222
-23
lines changed

8 files changed

+222
-23
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ build :; forge clean && forge build --optimize --optimizer-runs 1000000
1717
# Tests
1818
tests :; forge clean && forge test --mt test --optimize --optimizer-runs 1000000 -v # --ffi # enable if you need the `ffi` cheat code on HEVM
1919
test-with-gas-report :; forge clean && forge build && forge test --mt test --optimize --optimizer-runs 1000000 -v --gas-report # --ffi # enable if you need the `ffi` cheat code on HEVM
20+
test-unit :; forge clean && forge test --no-match-test invariant --optimize --optimizer-runs 1000000 -v # --ffi # enable if you need the `ffi` cheat code on HEVM
2021
test-invariant :; ./test/invariants/test-invariant.sh ${SCENARIO} ${NUM_ACTORS} ${NUM_PROPOSALS} ${PER_ADDRESS_TOKEN_REQ_CAP}
2122
test-invariant-all :; forge clean && forge t --mt invariant
2223
test-invariant-multiple-distribution :; forge clean && ./test/invariants/test-invariant.sh MultipleDistribution 2 25 200

script/GrantFund.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ contract DeployGrantFund is Script {
2222
vm.stopBroadcast();
2323

2424
console.log("GrantFund deployed to %s", grantFund);
25-
console.log("Please transfer %s AJNA (%s WAD) into the treasury", treasury / 1e18, treasury);
25+
console.log("Please transfer %s AJNA (%s WAD) to the treasury using the fundTreasury() method found in GrantFund.sol", treasury / 1e18, treasury);
2626
}
2727
}

src/grants/GrantFund.sol

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ contract GrantFund is IGrantFund, Storage, ReentrancyGuard {
277277

278278
uint24 distributionId = proposal.distributionId;
279279

280-
// check that the distribution period has ended, and one week has passed to enable competing slates to be checked
280+
// check that the distribution period has ended
281281
if (block.number <= _distributions[distributionId].endBlock) revert ExecuteProposalInvalid();
282282

283283
// check proposal is successful and hasn't already been executed
@@ -307,8 +307,8 @@ contract GrantFund is IGrantFund, Storage, ReentrancyGuard {
307307

308308
DistributionPeriod storage currentDistribution = _distributions[_currentDistributionId];
309309

310-
// cannot add new proposal after end of screening period
311-
// screening period ends 72000 blocks before end of distribution period, ~ 80 days.
310+
// cannot add new proposal after the screening period ends
311+
// screening period ends 525_600 blocks after the start of the distribution period, ~73 days.
312312
if (block.number > _getScreeningStageEndBlock(currentDistribution.startBlock)) revert ScreeningPeriodEnded();
313313

314314
// store new proposal information
@@ -732,7 +732,7 @@ contract GrantFund is IGrantFund, Storage, ReentrancyGuard {
732732
* @dev Votes can be allocated to multiple proposals, quadratically, for or against.
733733
* @param currentDistribution_ The current distribution period.
734734
* @param proposal_ The current proposal being voted upon.
735-
* @param voter_ The voter data struct tracking available votes.
735+
* @param voter_ The VoterInfo struct tracking votes.
736736
* @param voteParams_ The amount of votes being allocated to the proposal. Not squared. If less than 0, vote is against.
737737
* @return incrementalVotesUsed_ The amount of funding stage votes allocated to the proposal.
738738
*/
@@ -825,6 +825,7 @@ contract GrantFund is IGrantFund, Storage, ReentrancyGuard {
825825
/**
826826
* @notice Vote on a proposal in the screening stage of the Distribution Period.
827827
* @param proposal_ The current proposal being voted upon.
828+
* @param voter_ The VoterInfo struct tracking votes.
828829
* @param votes_ The amount of votes being cast.
829830
*/
830831
function _screeningVote(
@@ -1098,6 +1099,8 @@ contract GrantFund is IGrantFund, Storage, ReentrancyGuard {
10981099
DistributionPeriod storage currentDistribution = _distributions[distributionId_];
10991100
VoterInfo storage voter = _voterInfo[distributionId_][voter_];
11001101

1102+
if (voter.screeningVotesCast == 0) return 0;
1103+
11011104
rewards_ = _getDelegateReward(currentDistribution, voter);
11021105
}
11031106

src/grants/interfaces/IGrantFundActions.sol

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ interface IGrantFundActions is IGrantFundState {
6868
* @param targets_ The addresses of the contracts to call.
6969
* @param values_ The amounts of ETH to send to each target.
7070
* @param calldatas_ The calldata to send to each target.
71-
* @param descriptionHash_ The hash of the proposal's description string. Generated by `abi.encode(DESCRIPTION_PREFIX_HASH, keccak256(bytes(description_))`.
72-
* The `DESCRIPTION_PREFIX_HASH` is unique for each funding mechanism: `keccak256(bytes("Standard Funding: "))` for standard funding
71+
* @param descriptionHash_ The hash of the proposal's description string. Generated by `keccak256(bytes(description_))` or by calling `getDescriptionHash`.
7372
* @return proposalId_ The hashed proposalId created from the provided params.
7473
*/
7574
function hashProposal(
@@ -81,7 +80,7 @@ interface IGrantFundActions is IGrantFundState {
8180

8281
/**
8382
* @notice Submit a new proposal to the Grant Coordination Fund Standard Funding mechanism.
84-
* @dev All proposals can be submitted by anyone. There can only be one value in each array. Interface is compliant with OZ.propose().
83+
* @dev Proposals can be submitted by anyone. Interface is compliant with OZ.propose().
8584
* @param targets_ List of contracts the proposal calldata will interact with. Should be the Ajna token contract for all proposals.
8685
* @param values_ List of values to be sent with the proposal calldata. Should be 0 for all proposals.
8786
* @param calldatas_ List of calldata to be executed. Should be the transfer() method.
@@ -97,7 +96,7 @@ interface IGrantFundActions is IGrantFundState {
9796

9897
/**
9998
* @notice Find the status of a given proposal.
100-
* @dev Check proposal status based upon Grant Fund specific logic.
99+
* @dev Proposal status depends on the stage of the distribution period in which it was submitted, and vote counts on the proposal.
101100
* @param proposalId_ The id of the proposal to query the status of.
102101
* @return ProposalState of the given proposal.
103102
*/
@@ -106,7 +105,7 @@ interface IGrantFundActions is IGrantFundState {
106105
) external view returns (ProposalState);
107106

108107
/**
109-
* @notice Check if a slate of proposals meets requirements, and maximizes votes. If so, update DistributionPeriod.
108+
* @notice Check if a slate of proposals meets requirements, and maximizes votes. If so, set the provided proposal slate as the new top slate of proposals.
110109
* @param proposalIds_ Array of proposal Ids to check.
111110
* @param distributionId_ Id of the current distribution period.
112111
* @return newTopSlate_ Boolean indicating whether the new proposal slate was set as the new top slate for distribution.
@@ -122,7 +121,7 @@ interface IGrantFundActions is IGrantFundState {
122121

123122
/**
124123
* @notice Cast an array of funding votes in one transaction.
125-
* @dev Calls out to StandardFunding._fundingVote().
124+
* @dev Calls StandardFunding._fundingVote().
126125
* @dev Only iterates through a maximum of 10 proposals that made it through the screening round.
127126
* @dev Counters incremented in an unchecked block due to being bounded by array length.
128127
* @param voteParams_ The array of votes on proposals to cast.
@@ -134,7 +133,7 @@ interface IGrantFundActions is IGrantFundState {
134133

135134
/**
136135
* @notice Cast an array of screening votes in one transaction.
137-
* @dev Calls out to StandardFunding._screeningVote().
136+
* @dev Calls StandardFunding._screeningVote().
138137
* @dev Counters incremented in an unchecked block due to being bounded by array length.
139138
* @param voteParams_ The array of votes on proposals to cast.
140139
* @return votesCast_ The total number of votes cast across all of the proposals.
@@ -167,9 +166,9 @@ interface IGrantFundActions is IGrantFundState {
167166

168167
/**
169168
* @notice Calculate the description hash of a proposal.
170-
* @dev The description hash is used as a unique identifier for a proposal. It is created by hashing the description string with a prefix.
169+
* @dev The description hash is used as a unique identifier for a proposal. It is created by hashing the description string.
171170
* @param description_ The proposal's description string.
172-
* @return The hash of the proposal's prefix and description string.
171+
* @return The hash of the proposal's description string.
173172
*/
174173
function getDescriptionHash(string memory description_) external pure returns (bytes32);
175174

@@ -204,7 +203,7 @@ interface IGrantFundActions is IGrantFundState {
204203

205204
/**
206205
* @notice Get the block number at which this distribution period's funding stage ends.
207-
* @param startBlock_ The end block of a distribution period to get the funding stage end block for.
206+
* @param startBlock_ The start block of a distribution period to get the funding stage end block for.
208207
* @return The block number at which this distribution period's funding stage ends.
209208
*/
210209
function getFundingStageEndBlock(uint256 startBlock_) external pure returns (uint256);

src/grants/interfaces/IGrantFundErrors.sol

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ interface IGrantFundErrors {
1212
/*** Errors ***/
1313
/**************/
1414

15-
/**
16-
* @notice Voter has already voted on a proposal in the screening stage in a quarter.
17-
*/
18-
error AlreadyVoted();
19-
2015
/**
2116
* @notice User attempted to start a new distribution or claim delegation rewards before the distribution period ended.
2217
*/

src/token/BurnWrapper.sol

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,32 @@ import { ERC20Permit } from "@oz/token/ERC20/extensions/draft-ERC20Permit.sol
1111
import { ERC20Wrapper } from "@oz/token/ERC20/extensions/ERC20Wrapper.sol";
1212
import { IERC20Metadata } from "@oz/token/ERC20/extensions/IERC20Metadata.sol";
1313

14+
/**
15+
* @title Ajna Token `ERC20` token interface.
16+
* @dev Ajna Token `ERC20` token interface, including the following functions:
17+
* - `burnFrom()`
18+
* @dev Used by the `BurnWrappedAjna` contract to burn Ajna tokens on wrapping.
19+
*/
20+
interface IERC20Token {
21+
/**
22+
* @notice Burns `amount` tokens from `account`, deducting from the caller's allowance and balance.
23+
* @param account Account to burn tokens from.
24+
* @param amount Amount of tokens to burn.
25+
*/
26+
function burnFrom(address account, uint256 amount) external;
27+
}
28+
29+
30+
/**
31+
* @title BurnWrappedAjna Contract
32+
* @notice Entrypoint of BurnWrappedAjna actions for Ajna token holders looking to migrate their tokens to a sidechain:
33+
* - `TokenHolders`: Approve the BurnWrappedAjna contract to burn a specified amount of Ajna tokens, and call `depositFor()` to mint them a corresponding amount of bwAJNA tokens.
34+
* @dev This contract is intended for usage in cases where users are attempting to migrate their Ajna to a sidechain that lacks a permissionless bridge.
35+
* Usage of this contract protects holders from the risk of a compromised sidechain bridge.
36+
* @dev Contract inherits from OpenZeppelin ERC20Burnable and ERC20Wrapper extensions.
37+
* @dev Only mainnet Ajna token can be wrapped. Tokens that have been wrapped cannot be unwrapped, as they are burned on wrapping.
38+
* @dev Holders must call `depositFor()` to wrap their tokens. Transferring Ajna tokens to the wrapper contract directly results in loss of tokens.
39+
*/
1440
contract BurnWrappedAjna is ERC20, ERC20Burnable, ERC20Permit, ERC20Wrapper {
1541

1642
/**
@@ -50,6 +76,18 @@ contract BurnWrappedAjna is ERC20, ERC20Burnable, ERC20Permit, ERC20Wrapper {
5076
return 18;
5177
}
5278

79+
/**
80+
* @notice Override wrap method to burn Ajna tokens on wrapping instead of transferring to the wrapper contract.
81+
*/
82+
function depositFor(address account, uint256 amount) public override returns (bool) {
83+
// burn the existing ajna tokens
84+
IERC20Token(AJNA_TOKEN_ADDRESS).burnFrom(account, amount);
85+
86+
// mint the new wrapped tokens
87+
_mint(account, amount);
88+
return true;
89+
}
90+
5391
/**
5492
* @notice Override unwrap method to ensure burn wrapped tokens can't be unwrapped.
5593
*/

0 commit comments

Comments
 (0)