Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAO accounting for the nouns fungible token #840

Open
wants to merge 11 commits into
base: verbs-poc-client-incentives-noracle
Choose a base branch
from
17 changes: 12 additions & 5 deletions packages/nouns-contracts/contracts/governance/NounsDAOAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,15 @@ library NounsDAOAdmin {
*/
function _setErc20TokensToIncludeInFork(address[] calldata erc20tokens) public onlyAdmin {
checkForDuplicates(erc20tokens);
address fungibleToken = ds().nounsFungibleToken;
if (fungibleToken != address(0)) {
for (uint256 i = 0; i < erc20tokens.length; i++) {
require(
erc20tokens[i] != fungibleToken,
'NounsDAO::_setErc20TokensToIncludeInFork: cannot include fungible token'
);
}
}

emit ERC20TokensToIncludeInForkSet(ds().erc20TokensToIncludeInFork, erc20tokens);

Expand Down Expand Up @@ -537,12 +546,10 @@ library NounsDAOAdmin {
}

/**
* @notice Admin function for zeroing out the state variable `voteSnapshotBlockSwitchProposalId`
* @dev We want to zero-out this state slot so we can remove this temporary variable from contract code and
* be ready to reuse this slot.
* @notice Admin function for setting the canonical Nouns Fungible Token address
*/
function _zeroOutVoteSnapshotBlockSwitchProposalId() external onlyAdmin {
ds().voteSnapshotBlockSwitchProposalId = 0;
function _setNounsFungibleToken(address newNounsFungibleToken) public onlyAdmin {
ds().nounsFungibleToken = newNounsFungibleToken;
eladmallel marked this conversation as resolved.
Show resolved Hide resolved
}

function _writeQuorumParamsCheckpoint(NounsDAOTypes.DynamicQuorumParams memory params) internal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,8 @@ interface NounsDAOTypes {
uint256 forkThresholdBPS;
/// @notice Address of the original timelock
INounsDAOExecutor timelockV1;
/// @dev Make sure this stays the last variable in this struct, so we can delete it in the next version
/// @dev To be zeroed-out in the upcoming DAO upgrade.
uint256 voteSnapshotBlockSwitchProposalId;
/// @notice The address of the Nouns-backed ERC20 token
address nounsFungibleToken;
}

struct Proposal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -925,9 +925,5 @@ contract NounsDAOLogicV4 is NounsDAOStorage, NounsDAOEventsV3 {
return address(ds.timelockV1);
}

eladmallel marked this conversation as resolved.
Show resolved Hide resolved
function voteSnapshotBlockSwitchProposalId() public view returns (uint256) {
return ds.voteSnapshotBlockSwitchProposalId;
}

receive() external payable {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { NounsDAOTypes, INounsDAOForkEscrow, INounsDAOExecutorV2 } from '../Noun
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { NounsTokenFork } from './newdao/token/NounsTokenFork.sol';

interface NounsFungibleToken {
function balanceToBackingNFTCount(address account) external view returns (uint256);
}

library NounsDAOFork {
error ForkThresholdNotMet();
error ForkPeriodNotActive();
Expand Down Expand Up @@ -220,7 +224,16 @@ library NounsDAOFork {
* This is used when calculating proposal threshold, quorum, fork threshold & treasury split.
*/
function adjustedTotalSupply(NounsDAOTypes.Storage storage ds) internal view returns (uint256) {
eladmallel marked this conversation as resolved.
Show resolved Hide resolved
return ds.nouns.totalSupply() - ds.nouns.balanceOf(address(ds.timelock)) - ds.forkEscrow.numTokensOwnedByDAO();
uint256 nounsHeldViaFungibleToken = 0;
address tokenAddress = ds.nounsFungibleToken;
if (tokenAddress != address(0)) {
nounsHeldViaFungibleToken = NounsFungibleToken(tokenAddress).balanceToBackingNFTCount(address(ds.timelock));
}
return
ds.nouns.totalSupply() -
ds.nouns.balanceOf(address(ds.timelock)) -
ds.forkEscrow.numTokensOwnedByDAO() -
nounsHeldViaFungibleToken;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,18 +349,6 @@ contract NounsDAOLogicAdminTest is NounsDAOLogicBaseTest {
assertEq(dao.forkThresholdBPS(), 42);
}

function test__zeroOutVoteSnapshotBlockSwitchProposalId_onlyAdmin() public {
vm.expectRevert(NounsDAOAdmin.AdminOnly.selector);
dao._zeroOutVoteSnapshotBlockSwitchProposalId();
}

function test__zeroOutVoteSnapshotBlockSwitchProposalId_works() public {
vm.prank(address(dao.timelock()));
dao._zeroOutVoteSnapshotBlockSwitchProposalId();

assertEq(dao.voteSnapshotBlockSwitchProposalId(), 0);
}

function test_setErc20TokensToIncludeInFork_onlyAdmin() public {
tokens = [address(1), address(2)];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ abstract contract UpgradeMainnetForkBaseTest is Test {
vm.deal(address(NOUNS_DAO_PROXY_MAINNET), 100 ether);
vm.fee(50 gwei);
vm.txGasPrice(50 gwei);

// zero-out the slot where `voteSnapshotBlockSwitchProposalId` was
// as will happen in the upcoming upgrade
// so that `nounsFungibleToken` will be address(0) by default
vm.store(address(NOUNS_DAO_PROXY_MAINNET), bytes32(uint256(0x19)), bytes32(uint256(0)));
}

function propose(
Expand Down Expand Up @@ -177,20 +182,6 @@ contract DAOUpgradeMainnetForkTest is UpgradeMainnetForkBaseTest {
assertEq(expectedVotingDelay, NOUNS_DAO_PROXY_MAINNET.votingDelay());
}

function test_voteSnapshotBlockSwitchProposalId_zeroOutWorks() public {
assertNotEq(NOUNS_DAO_PROXY_MAINNET.voteSnapshotBlockSwitchProposalId(), 0);

uint256 proposalId = propose(
address(NOUNS_DAO_PROXY_MAINNET),
0,
'_zeroOutVoteSnapshotBlockSwitchProposalId()',
''
);
voteAndExecuteProposal(proposalId);

assertEq(NOUNS_DAO_PROXY_MAINNET.voteSnapshotBlockSwitchProposalId(), 0);
}

function test_clientId_savedOnProposals() public {
uint32 expectedClientId = 42;
uint256 proposalId = propose(address(NOUNS_DAO_PROXY_MAINNET), 0, '', '', expectedClientId);
Expand Down
Loading