Contract Name:
NounsDAOLogicV1Fork
Contract Source Code:
// SPDX-License-Identifier: BSD-3-Clause
/// @title The Nouns DAO logic version 1
/*********************************
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░██░░░████░░██░░░████░░░ *
* ░░██████░░░████████░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
*********************************/
// LICENSE
// NounsDAOLogicV1Fork.sol is a modified version of NounsDAOLogicV1.sol.
// NounsDAOLogicV1.sol is a modified version of Compound Lab's GovernorBravoDelegate.sol:
// https://github.com/compound-finance/compound-protocol/blob/b9b14038612d846b83f8a009a82c38974ff2dcfe/contracts/Governance/GovernorBravoDelegate.sol
//
// GovernorBravoDelegate.sol source code Copyright 2020 Compound Labs, Inc. licensed under the BSD-3-Clause license.
// With modifications by Nounders DAO.
//
// Additional conditions of BSD-3-Clause can be found here: https://opensource.org/licenses/BSD-3-Clause
//
// MODIFICATIONS
// NounsDAOLogicV1Fork adds:
// - `quit(tokenIds)`, a function that allows token holders to quit the DAO, taking their pro rata funds,
// and sending their tokens to the DAO treasury.
//
// - `adjustedTotalSupply`, the total supply calculation used in DAO functions like quorum and proposal threshold, in
// which the DAO exludes tokens held by the treasury, such that tokens used to quit the DAO are not counted.
//
// - A function for the DAO to set which ERC20s are transferred pro rata in the `quit` function.
//
// - A new proposals getter function, since adding new fields to Proposal results in the default getter hitting a
// `Stack too deep` error.
//
// - A new Proposal field: `creationBlock`, used to resolve the `votingDelay` bug, in which editing `votingDelay` would
// change the votes snapshot block for proposals in-progress.
//
// NounsDAOLogicV1Fork modifies:
// - The proxy pattern from Compound's old Transparent-like proxy, to OpenZeppelin's recommended UUPS pattern.
//
// - `propose`
// - uses `adjustedTotalSupply`
// - includes a new 'delayed governance' feature which gives forkers from the original DAO time to claim their tokens
// with this new DAO; proposals are not allowed until all tokens are claimed, or until the delay expiration
// timestamp is reached.
//
// - `cancel` bugfix, allowing proposals to be canceled by anyone if the proposer's vote balance is equal to proposal
// threshold.
//
// - Removes the vetoer role and logic related to it. The quit function provides minority protection instead of the
// vetoer, and fork DAOs can upgrade their governor to include the vetoer feature if it's needed.
//
// - Modified MIN_VOTING_PERIOD, MAX_VOTING_PERIOD to correct block numbers assuming 12 second blocks
// - Modified MAX_VOTING_DELAY to be 2 weeks
//
// NounsDAOLogicV1 adds:
// - Proposal Threshold basis points instead of fixed number
// due to the Noun token's increasing supply
//
// - Quorum Votes basis points instead of fixed number
// due to the Noun token's increasing supply
//
// - Per proposal storing of fixed `proposalThreshold`
// and `quorumVotes` calculated using the Noun token's total supply
// at the block the proposal was created and the basis point parameters
//
// - `ProposalCreatedWithRequirements` event that emits `ProposalCreated` parameters with
// the addition of `proposalThreshold` and `quorumVotes`
//
// - Votes are counted from the block a proposal is created instead of
// the proposal's voting start block to align with the parameters
// stored with the proposal
//
// - Veto ability which allows `vetoer` to halt any proposal at any stage unless
// the proposal is executed.
// The `veto(uint proposalId)` logic is a modified version of `cancel(uint proposalId)`
// A `vetoed` flag was added to the `Proposal` struct to support this.
//
// NounsDAOLogicV1 removes:
// - `initialProposalId` and `_initiate()` due to this being the
// first instance of the governance contract unlike
// GovernorBravo which upgrades GovernorAlpha
//
// - Value passed along using `timelock.executeTransaction{value: proposal.value}`
// in `execute(uint proposalId)`. This contract should not hold funds and does not
// implement `receive()` or `fallback()` functions.
//
pragma solidity ^0.8.19;
import { UUPSUpgradeable } from '@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol';
import { NounsDAOEventsFork } from './NounsDAOEventsFork.sol';
import { NounsDAOStorageV1Fork } from './NounsDAOStorageV1Fork.sol';
import { NounsDAOExecutorV2 } from '../../../NounsDAOExecutorV2.sol';
import { INounsTokenForkLike } from './INounsTokenForkLike.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { ReentrancyGuardUpgradeable } from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
contract NounsDAOLogicV1Fork is UUPSUpgradeable, ReentrancyGuardUpgradeable, NounsDAOStorageV1Fork, NounsDAOEventsFork {
error AdminOnly();
error WaitingForTokensToClaimOrExpiration();
error TokensMustBeASubsetOfWhitelistedTokens();
error GovernanceBlockedDuringForkingPeriod();
error DuplicateTokenAddress();
event ERC20TokensToIncludeInQuitSet(address[] oldErc20Tokens, address[] newErc20tokens);
event Quit(address indexed msgSender, uint256[] tokenIds);
/// @notice The name of this contract
string public constant name = 'Nouns DAO';
/// @notice The minimum setable proposal threshold
uint256 public constant MIN_PROPOSAL_THRESHOLD_BPS = 1; // 1 basis point or 0.01%
/// @notice The maximum setable proposal threshold
uint256 public constant MAX_PROPOSAL_THRESHOLD_BPS = 1_000; // 1,000 basis points or 10%
/// @notice The minimum setable voting period
uint256 public constant MIN_VOTING_PERIOD = 7_200; // 24 hours
/// @notice The max setable voting period
uint256 public constant MAX_VOTING_PERIOD = 100_800; // 2 weeks
/// @notice The min setable voting delay
uint256 public constant MIN_VOTING_DELAY = 1;
/// @notice The max setable voting delay
uint256 public constant MAX_VOTING_DELAY = 100_800; // 2 weeks
/// @notice The minimum setable quorum votes basis points
uint256 public constant MIN_QUORUM_VOTES_BPS = 200; // 200 basis points or 2%
/// @notice The maximum setable quorum votes basis points
uint256 public constant MAX_QUORUM_VOTES_BPS = 2_000; // 2,000 basis points or 20%
/// @notice The maximum number of actions that can be included in a proposal
uint256 public constant proposalMaxOperations = 10; // 10 actions
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH =
keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');
/// @notice The EIP-712 typehash for the ballot struct used by the contract
bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');
constructor() initializer {}
/**
* @notice Used to initialize the contract during delegator contructor
* @dev Not asserting that param values are within the hard-coded bounds in order to make it easier to run
* manual tests; seems a safe decision since we assume fork DAOs are initialized by `ForkDAODeployer`
* @param timelock_ The address of the NounsDAOExecutor
* @param nouns_ The address of the NOUN tokens
* @param votingPeriod_ The initial voting period
* @param votingDelay_ The initial voting delay
* @param proposalThresholdBPS_ The initial proposal threshold in basis points
* @param quorumVotesBPS_ The initial quorum votes threshold in basis points
* @param erc20TokensToIncludeInQuit_ The initial list of ERC20 tokens to include when quitting
* @param delayedGovernanceExpirationTimestamp_ The delayed governance expiration timestamp
*/
function initialize(
address timelock_,
address nouns_,
uint256 votingPeriod_,
uint256 votingDelay_,
uint256 proposalThresholdBPS_,
uint256 quorumVotesBPS_,
address[] memory erc20TokensToIncludeInQuit_,
uint256 delayedGovernanceExpirationTimestamp_
) public virtual {
__ReentrancyGuard_init_unchained();
require(address(timelock) == address(0), 'NounsDAO::initialize: can only initialize once');
require(timelock_ != address(0), 'NounsDAO::initialize: invalid timelock address');
require(nouns_ != address(0), 'NounsDAO::initialize: invalid nouns address');
emit VotingPeriodSet(votingPeriod, votingPeriod_);
emit VotingDelaySet(votingDelay, votingDelay_);
emit ProposalThresholdBPSSet(proposalThresholdBPS, proposalThresholdBPS_);
emit QuorumVotesBPSSet(quorumVotesBPS, quorumVotesBPS_);
admin = timelock_;
timelock = NounsDAOExecutorV2(payable(timelock_));
nouns = INounsTokenForkLike(nouns_);
votingPeriod = votingPeriod_;
votingDelay = votingDelay_;
proposalThresholdBPS = proposalThresholdBPS_;
quorumVotesBPS = quorumVotesBPS_;
erc20TokensToIncludeInQuit = erc20TokensToIncludeInQuit_;
delayedGovernanceExpirationTimestamp = delayedGovernanceExpirationTimestamp_;
}
/**
* @notice A function that allows token holders to quit the DAO, taking their pro rata funds,
* and sending their tokens to the DAO treasury.
* Will revert as long as not all tokens were claimed, and as long as the delayed governance has not expired.
* @param tokenIds The token ids to quit with
*/
function quit(uint256[] calldata tokenIds) external nonReentrant {
quitInternal(tokenIds, erc20TokensToIncludeInQuit);
}
function quit(uint256[] calldata tokenIds, address[] calldata erc20TokensToInclude) external nonReentrant {
checkForDuplicates(erc20TokensToInclude);
// check that erc20TokensToInclude is a subset of `erc20TokensToIncludeInQuit`
address[] memory erc20TokensToIncludeInQuit_ = erc20TokensToIncludeInQuit;
for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
if (!isAddressIn(erc20TokensToInclude[i], erc20TokensToIncludeInQuit_)) {
revert TokensMustBeASubsetOfWhitelistedTokens();
}
}
quitInternal(tokenIds, erc20TokensToInclude);
}
function quitInternal(uint256[] calldata tokenIds, address[] memory erc20TokensToInclude) internal {
checkGovernanceActive();
uint256 totalSupply = adjustedTotalSupply();
for (uint256 i = 0; i < tokenIds.length; i++) {
nouns.transferFrom(msg.sender, address(timelock), tokenIds[i]);
}
uint256[] memory balancesToSend = new uint256[](erc20TokensToInclude.length);
// Capture balances to send before actually sending them, to avoid the risk of external calls changing balances.
uint256 ethToSend = (address(timelock).balance * tokenIds.length) / totalSupply;
for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
IERC20 erc20token = IERC20(erc20TokensToInclude[i]);
balancesToSend[i] = (erc20token.balanceOf(address(timelock)) * tokenIds.length) / totalSupply;
}
// Send ETH and ERC20 tokens
timelock.sendETH(payable(msg.sender), ethToSend);
for (uint256 i = 0; i < erc20TokensToInclude.length; i++) {
if (balancesToSend[i] > 0) {
timelock.sendERC20(msg.sender, erc20TokensToInclude[i], balancesToSend[i]);
}
}
emit Quit(msg.sender, tokenIds);
}
function isAddressIn(address a, address[] memory addresses) internal pure returns (bool) {
for (uint256 i = 0; i < addresses.length; i++) {
if (addresses[i] == a) return true;
}
return false;
}
struct ProposalTemp {
uint256 totalSupply;
uint256 proposalThreshold;
uint256 latestProposalId;
uint256 startBlock;
uint256 endBlock;
}
/**
* @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold
* Will revert as long as not all tokens were claimed, and as long as the delayed governance has not expired.
* @param targets Target addresses for proposal calls
* @param values Eth values for proposal calls
* @param signatures Function signatures for proposal calls
* @param calldatas Calldatas for proposal calls
* @param description String description of the proposal
* @return Proposal id of new proposal
*/
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) public returns (uint256) {
checkGovernanceActive();
ProposalTemp memory temp;
temp.totalSupply = adjustedTotalSupply();
temp.proposalThreshold = bps2Uint(proposalThresholdBPS, temp.totalSupply);
require(
nouns.getPriorVotes(msg.sender, block.number - 1) > temp.proposalThreshold,
'NounsDAO::propose: proposer votes below proposal threshold'
);
require(
targets.length == values.length &&
targets.length == signatures.length &&
targets.length == calldatas.length,
'NounsDAO::propose: proposal function information arity mismatch'
);
require(targets.length != 0, 'NounsDAO::propose: must provide actions');
require(targets.length <= proposalMaxOperations, 'NounsDAO::propose: too many actions');
temp.latestProposalId = latestProposalIds[msg.sender];
if (temp.latestProposalId != 0) {
ProposalState proposersLatestProposalState = state(temp.latestProposalId);
require(
proposersLatestProposalState != ProposalState.Active,
'NounsDAO::propose: one live proposal per proposer, found an already active proposal'
);
require(
proposersLatestProposalState != ProposalState.Pending,
'NounsDAO::propose: one live proposal per proposer, found an already pending proposal'
);
}
temp.startBlock = block.number + votingDelay;
temp.endBlock = temp.startBlock + votingPeriod;
proposalCount++;
Proposal storage newProposal = _proposals[proposalCount];
newProposal.id = proposalCount;
newProposal.proposer = msg.sender;
newProposal.proposalThreshold = temp.proposalThreshold;
newProposal.quorumVotes = bps2Uint(quorumVotesBPS, temp.totalSupply);
newProposal.eta = 0;
newProposal.targets = targets;
newProposal.values = values;
newProposal.signatures = signatures;
newProposal.calldatas = calldatas;
newProposal.startBlock = temp.startBlock;
newProposal.endBlock = temp.endBlock;
newProposal.forVotes = 0;
newProposal.againstVotes = 0;
newProposal.abstainVotes = 0;
newProposal.canceled = false;
newProposal.executed = false;
newProposal.creationBlock = block.number;
latestProposalIds[newProposal.proposer] = newProposal.id;
/// @notice Maintains backwards compatibility with GovernorBravo events
emit ProposalCreated(
newProposal.id,
msg.sender,
targets,
values,
signatures,
calldatas,
newProposal.startBlock,
newProposal.endBlock,
description
);
/// @notice Updated event with `proposalThreshold` and `quorumVotes`
emit ProposalCreatedWithRequirements(
newProposal.id,
msg.sender,
targets,
values,
signatures,
calldatas,
newProposal.startBlock,
newProposal.endBlock,
newProposal.proposalThreshold,
newProposal.quorumVotes,
description
);
return newProposal.id;
}
/**
* @notice Internal function that reverts if the governance is not active yet. Governance becomes active as soon as
* the forking period ended and one of these conditions is met:
* 1. All tokens are claimed
* 2. The delayed governance expiration timestamp is reached
*/
function checkGovernanceActive() internal view {
if (block.timestamp < nouns.forkingPeriodEndTimestamp()) {
revert GovernanceBlockedDuringForkingPeriod();
}
if (block.timestamp < delayedGovernanceExpirationTimestamp && nouns.remainingTokensToClaim() > 0) {
revert WaitingForTokensToClaimOrExpiration();
}
}
/**
* @notice Queues a proposal of state succeeded
* @param proposalId The id of the proposal to queue
*/
function queue(uint256 proposalId) external {
require(
state(proposalId) == ProposalState.Succeeded,
'NounsDAO::queue: proposal can only be queued if it is succeeded'
);
Proposal storage proposal = _proposals[proposalId];
uint256 eta = block.timestamp + timelock.delay();
for (uint256 i = 0; i < proposal.targets.length; i++) {
queueOrRevertInternal(
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
eta
);
}
proposal.eta = eta;
emit ProposalQueued(proposalId, eta);
}
function queueOrRevertInternal(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) internal {
require(
!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))),
'NounsDAO::queueOrRevertInternal: identical proposal action already queued at eta'
);
timelock.queueTransaction(target, value, signature, data, eta);
}
/**
* @notice Executes a queued proposal if eta has passed
* @param proposalId The id of the proposal to execute
*/
function execute(uint256 proposalId) external {
require(
state(proposalId) == ProposalState.Queued,
'NounsDAO::execute: proposal can only be executed if it is queued'
);
Proposal storage proposal = _proposals[proposalId];
proposal.executed = true;
for (uint256 i = 0; i < proposal.targets.length; i++) {
timelock.executeTransaction(
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
proposal.eta
);
}
emit ProposalExecuted(proposalId);
}
/**
* @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold
* @param proposalId The id of the proposal to cancel
*/
function cancel(uint256 proposalId) external {
require(state(proposalId) != ProposalState.Executed, 'NounsDAO::cancel: cannot cancel executed proposal');
Proposal storage proposal = _proposals[proposalId];
require(
msg.sender == proposal.proposer ||
nouns.getPriorVotes(proposal.proposer, block.number - 1) <= proposal.proposalThreshold,
'NounsDAO::cancel: proposer above threshold'
);
proposal.canceled = true;
for (uint256 i = 0; i < proposal.targets.length; i++) {
timelock.cancelTransaction(
proposal.targets[i],
proposal.values[i],
proposal.signatures[i],
proposal.calldatas[i],
proposal.eta
);
}
emit ProposalCanceled(proposalId);
}
/**
* @notice Gets actions of a proposal
* @param proposalId the id of the proposal
* @return targets
* @return values
* @return signatures
* @return calldatas
*/
function getActions(uint256 proposalId)
external
view
returns (
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas
)
{
Proposal storage p = _proposals[proposalId];
return (p.targets, p.values, p.signatures, p.calldatas);
}
/**
* @notice Gets the receipt for a voter on a given proposal
* @param proposalId the id of proposal
* @param voter The address of the voter
* @return The voting receipt
*/
function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory) {
return _proposals[proposalId].receipts[voter];
}
/**
* @notice Gets the state of a proposal
* @param proposalId The id of the proposal
* @return Proposal state
*/
function state(uint256 proposalId) public view returns (ProposalState) {
require(proposalCount >= proposalId, 'NounsDAO::state: invalid proposal id');
Proposal storage proposal = _proposals[proposalId];
if (proposal.canceled) {
return ProposalState.Canceled;
} else if (block.number <= proposal.startBlock) {
return ProposalState.Pending;
} else if (block.number <= proposal.endBlock) {
return ProposalState.Active;
} else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < proposal.quorumVotes) {
return ProposalState.Defeated;
} else if (proposal.eta == 0) {
return ProposalState.Succeeded;
} else if (proposal.executed) {
return ProposalState.Executed;
} else if (block.timestamp >= proposal.eta + timelock.GRACE_PERIOD()) {
return ProposalState.Expired;
} else {
return ProposalState.Queued;
}
}
/**
* @notice Returns the proposal details given a proposal id.
* @dev this explicit getter solves the `Stack too deep` problem that arose after
* adding a new field to the Proposal struct.
* @param proposalId the proposal id to get the data for
* @return A `ProposalCondensed` struct with the proposal data
*/
function proposals(uint256 proposalId) external view returns (ProposalCondensed memory) {
Proposal storage proposal = _proposals[proposalId];
return
ProposalCondensed({
id: proposal.id,
proposer: proposal.proposer,
proposalThreshold: proposal.proposalThreshold,
quorumVotes: proposal.quorumVotes,
eta: proposal.eta,
startBlock: proposal.startBlock,
endBlock: proposal.endBlock,
forVotes: proposal.forVotes,
againstVotes: proposal.againstVotes,
abstainVotes: proposal.abstainVotes,
canceled: proposal.canceled,
executed: proposal.executed,
creationBlock: proposal.creationBlock
});
}
/**
* @notice Cast a vote for a proposal
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
*/
function castVote(uint256 proposalId, uint8 support) external {
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), '');
}
/**
* @notice Cast a vote for a proposal with a reason
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @param reason The reason given for the vote by the voter
*/
function castVoteWithReason(
uint256 proposalId,
uint8 support,
string calldata reason
) external {
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
}
/**
* @notice Cast a vote for a proposal by signature
* @dev External function that accepts EIP-712 signatures for voting on proposals.
*/
function castVoteBySig(
uint256 proposalId,
uint8 support,
uint8 v,
bytes32 r,
bytes32 s
) external {
bytes32 domainSeparator = keccak256(
abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), block.chainid, address(this))
);
bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash));
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), 'NounsDAO::castVoteBySig: invalid signature');
emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), '');
}
/**
* @notice Internal function that caries out voting logic
* @param voter The voter that is casting their vote
* @param proposalId The id of the proposal to vote on
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
* @return The number of votes cast
*/
function castVoteInternal(
address voter,
uint256 proposalId,
uint8 support
) internal returns (uint96) {
require(state(proposalId) == ProposalState.Active, 'NounsDAO::castVoteInternal: voting is closed');
require(support <= 2, 'NounsDAO::castVoteInternal: invalid vote type');
Proposal storage proposal = _proposals[proposalId];
Receipt storage receipt = proposal.receipts[voter];
require(receipt.hasVoted == false, 'NounsDAO::castVoteInternal: voter already voted');
/// @notice: Unlike GovernerBravo, votes are considered from the block the proposal was created in order to normalize quorumVotes and proposalThreshold metrics
uint96 votes = nouns.getPriorVotes(voter, proposal.creationBlock);
if (support == 0) {
proposal.againstVotes = proposal.againstVotes + votes;
} else if (support == 1) {
proposal.forVotes = proposal.forVotes + votes;
} else if (support == 2) {
proposal.abstainVotes = proposal.abstainVotes + votes;
}
receipt.hasVoted = true;
receipt.support = support;
receipt.votes = votes;
return votes;
}
/**
* @notice Admin function for setting the voting delay
* @param newVotingDelay new voting delay, in blocks
*/
function _setVotingDelay(uint256 newVotingDelay) external {
require(msg.sender == admin, 'NounsDAO::_setVotingDelay: admin only');
require(
newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY,
'NounsDAO::_setVotingDelay: invalid voting delay'
);
uint256 oldVotingDelay = votingDelay;
votingDelay = newVotingDelay;
emit VotingDelaySet(oldVotingDelay, newVotingDelay);
}
/**
* @notice Admin function for setting the voting period
* @param newVotingPeriod new voting period, in blocks
*/
function _setVotingPeriod(uint256 newVotingPeriod) external {
require(msg.sender == admin, 'NounsDAO::_setVotingPeriod: admin only');
require(
newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD,
'NounsDAO::_setVotingPeriod: invalid voting period'
);
uint256 oldVotingPeriod = votingPeriod;
votingPeriod = newVotingPeriod;
emit VotingPeriodSet(oldVotingPeriod, newVotingPeriod);
}
/**
* @notice Admin function for setting the proposal threshold basis points
* @dev newProposalThresholdBPS must be greater than the hardcoded min
* @param newProposalThresholdBPS new proposal threshold
*/
function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external {
require(msg.sender == admin, 'NounsDAO::_setProposalThresholdBPS: admin only');
require(
newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS &&
newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS,
'NounsDAO::_setProposalThreshold: invalid proposal threshold'
);
uint256 oldProposalThresholdBPS = proposalThresholdBPS;
proposalThresholdBPS = newProposalThresholdBPS;
emit ProposalThresholdBPSSet(oldProposalThresholdBPS, newProposalThresholdBPS);
}
/**
* @notice Admin function for setting the quorum votes basis points
* @dev newQuorumVotesBPS must be greater than the hardcoded min
* @param newQuorumVotesBPS new proposal threshold
*/
function _setQuorumVotesBPS(uint256 newQuorumVotesBPS) external {
require(msg.sender == admin, 'NounsDAO::_setQuorumVotesBPS: admin only');
require(
newQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS && newQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS,
'NounsDAO::_setQuorumVotesBPS: invalid quorum votes basis points'
);
uint256 oldQuorumVotesBPS = quorumVotesBPS;
quorumVotesBPS = newQuorumVotesBPS;
emit QuorumVotesBPSSet(oldQuorumVotesBPS, newQuorumVotesBPS);
}
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
*/
function _setPendingAdmin(address newPendingAdmin) external {
// Check caller = admin
require(msg.sender == admin, 'NounsDAO::_setPendingAdmin: admin only');
// Save current value, if any, for inclusion in log
address oldPendingAdmin = pendingAdmin;
// Store pendingAdmin with value newPendingAdmin
pendingAdmin = newPendingAdmin;
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
}
/**
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
* @dev Admin function for pending admin to accept role and update admin
*/
function _acceptAdmin() external {
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)
require(msg.sender == pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only');
// Save current values for inclusion in log
address oldAdmin = admin;
address oldPendingAdmin = pendingAdmin;
// Store admin with value pendingAdmin
admin = pendingAdmin;
// Clear the pending value
pendingAdmin = address(0);
emit NewAdmin(oldAdmin, admin);
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
}
/**
* @notice Admin function for setting the list of ERC20 tokens to transfer on `quit`.
*/
function _setErc20TokensToIncludeInQuit(address[] calldata erc20tokens) external {
if (msg.sender != admin) revert AdminOnly();
checkForDuplicates(erc20tokens);
emit ERC20TokensToIncludeInQuitSet(erc20TokensToIncludeInQuit, erc20tokens);
erc20TokensToIncludeInQuit = erc20tokens;
}
/**
* @notice Current proposal threshold using Noun Total Supply
* Differs from `GovernerBravo` which uses fixed amount
*/
function proposalThreshold() public view returns (uint256) {
return bps2Uint(proposalThresholdBPS, adjustedTotalSupply());
}
/**
* @notice Current quorum votes using Noun Total Supply
* Differs from `GovernerBravo` which uses fixed amount
*/
function quorumVotes() public view returns (uint256) {
return bps2Uint(quorumVotesBPS, adjustedTotalSupply());
}
function adjustedTotalSupply() public view returns (uint256) {
return nouns.totalSupply() - nouns.balanceOf(address(timelock)) + nouns.remainingTokensToClaim();
}
function erc20TokensToIncludeInQuitArray() public view returns (address[] memory) {
return erc20TokensToIncludeInQuit;
}
function bps2Uint(uint256 bps, uint256 number) internal pure returns (uint256) {
return (number * bps) / 10000;
}
function _authorizeUpgrade(address) internal view override {
require(msg.sender == admin, 'NounsDAO::_authorizeUpgrade: admin only');
}
function checkForDuplicates(address[] calldata erc20tokens) internal pure {
if (erc20tokens.length == 0) return;
for (uint256 i = 0; i < erc20tokens.length - 1; i++) {
for (uint256 j = i + 1; j < erc20tokens.length; j++) {
if (erc20tokens[i] == erc20tokens[j]) revert DuplicateTokenAddress();
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
import "../ERC1967/ERC1967Upgrade.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is ERC1967Upgrade {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*/
function upgradeTo(address newImplementation) external virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, new bytes(0), false);
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, data, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
}
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.19;
contract NounsDAOEventsFork {
/// @notice An event emitted when a new proposal is created
event ProposalCreated(
uint256 indexed id,
address indexed proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 startBlock,
uint256 endBlock,
string description
);
/// @notice An event emitted when a new proposal is created, which includes additional information
event ProposalCreatedWithRequirements(
uint256 indexed id,
address indexed proposer,
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas,
uint256 startBlock,
uint256 endBlock,
uint256 proposalThreshold,
uint256 quorumVotes,
string description
);
/// @notice An event emitted when a vote has been cast on a proposal
/// @param voter The address which casted a vote
/// @param proposalId The proposal id which was voted on
/// @param support Support value for the vote. 0=against, 1=for, 2=abstain
/// @param votes Number of votes which were cast by the voter
/// @param reason The reason given for the vote by the voter
event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 support, uint256 votes, string reason);
/// @notice An event emitted when a proposal has been canceled
event ProposalCanceled(uint256 indexed id);
/// @notice An event emitted when a proposal has been queued in the NounsDAOExecutor
event ProposalQueued(uint256 indexed id, uint256 eta);
/// @notice An event emitted when a proposal has been executed in the NounsDAOExecutor
event ProposalExecuted(uint256 indexed id);
/// @notice An event emitted when the voting delay is set
event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay);
/// @notice An event emitted when the voting period is set
event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod);
/// @notice Emitted when implementation is changed
event NewImplementation(address oldImplementation, address newImplementation);
/// @notice Emitted when proposal threshold basis points is set
event ProposalThresholdBPSSet(uint256 oldProposalThresholdBPS, uint256 newProposalThresholdBPS);
/// @notice Emitted when quorum votes basis points is set
event QuorumVotesBPSSet(uint256 oldQuorumVotesBPS, uint256 newQuorumVotesBPS);
/// @notice Emitted when pendingAdmin is changed
event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
/// @notice Emitted when pendingAdmin is accepted, which means admin is updated
event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
}
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.19;
import { NounsDAOExecutorV2 } from '../../../NounsDAOExecutorV2.sol';
import { INounsTokenForkLike } from './INounsTokenForkLike.sol';
/**
* @title Storage for `NounsDAOLogicV1Fork`.
* @dev Based on NounsDAOStorageV1, with the following changes:
* - vetoer is removed.
* - Vetoed proposal state removed.
* - implementation is removed, instead it's stored in the ERC-1967 storage slot.
* - proposals renamed to _proposals to enable the explicit getter, which solves the stack too deep issue with the
* default getter.
* - creationBlock added to Proposal struct, similar to V2, to solve the votingDelay editing bug.
* @notice For future upgrades, do not change NounsDAOStorageV1Fork. Create a new
* contract which implements NounsDAOStorageV1Fork.
*/
contract NounsDAOStorageV1Fork {
/// @notice Administrator for this contract
address public admin;
/// @notice Pending administrator for this contract
address public pendingAdmin;
/// @notice The delay before voting on a proposal may take place, once proposed, in blocks
uint256 public votingDelay;
/// @notice The duration of voting on a proposal, in blocks
uint256 public votingPeriod;
/// @notice The basis point number of votes to exceed in order for a voter to become a proposer. *DIFFERS from GovernerBravo
uint256 public proposalThresholdBPS;
/// @notice The basis point number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed. *DIFFERS from GovernerBravo
uint256 public quorumVotesBPS;
/// @notice The total number of proposals
uint256 public proposalCount;
/// @notice The address of the Nouns DAO Executor NounsDAOExecutor
NounsDAOExecutorV2 public timelock;
/// @notice The address of the Nouns tokens
INounsTokenForkLike public nouns;
/// @notice The official record of all proposals ever proposed
mapping(uint256 => Proposal) public _proposals;
/// @notice The latest proposal for each proposer
mapping(address => uint256) public latestProposalIds;
uint256 public delayedGovernanceExpirationTimestamp;
address[] public erc20TokensToIncludeInQuit;
struct Proposal {
/// @notice Unique id for looking up a proposal
uint256 id;
/// @notice Creator of the proposal
address proposer;
/// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo
uint256 proposalThreshold;
/// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
uint256 quorumVotes;
/// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
uint256 eta;
/// @notice the ordered list of target addresses for calls to be made
address[] targets;
/// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made
uint256[] values;
/// @notice The ordered list of function signatures to be called
string[] signatures;
/// @notice The ordered list of calldata to be passed to each call
bytes[] calldatas;
/// @notice The block at which voting begins: holders must delegate their votes prior to this block
uint256 startBlock;
/// @notice The block at which voting ends: votes must be cast prior to this block
uint256 endBlock;
/// @notice Current number of votes in favor of this proposal
uint256 forVotes;
/// @notice Current number of votes in opposition to this proposal
uint256 againstVotes;
/// @notice Current number of votes for abstaining for this proposal
uint256 abstainVotes;
/// @notice Flag marking whether the proposal has been canceled
bool canceled;
/// @notice Flag marking whether the proposal has been executed
bool executed;
/// @notice Receipts of ballots for the entire set of voters
mapping(address => Receipt) receipts;
/// @notice The block at which this proposal was created
uint256 creationBlock;
}
/// @notice Ballot receipt record for a voter
struct Receipt {
/// @notice Whether or not a vote has been cast
bool hasVoted;
/// @notice Whether or not the voter supports the proposal or abstains
uint8 support;
/// @notice The number of votes the voter had, which were cast
uint96 votes;
}
/// @notice Possible states that a proposal may be in
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed
}
struct ProposalCondensed {
/// @notice Unique id for looking up a proposal
uint256 id;
/// @notice Creator of the proposal
address proposer;
/// @notice The number of votes needed to create a proposal at the time of proposal creation. *DIFFERS from GovernerBravo
uint256 proposalThreshold;
/// @notice The minimum number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed at the time of proposal creation. *DIFFERS from GovernerBravo
uint256 quorumVotes;
/// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds
uint256 eta;
/// @notice The block at which voting begins: holders must delegate their votes prior to this block
uint256 startBlock;
/// @notice The block at which voting ends: votes must be cast prior to this block
uint256 endBlock;
/// @notice Current number of votes in favor of this proposal
uint256 forVotes;
/// @notice Current number of votes in opposition to this proposal
uint256 againstVotes;
/// @notice Current number of votes for abstaining for this proposal
uint256 abstainVotes;
/// @notice Flag marking whether the proposal has been canceled
bool canceled;
/// @notice Flag marking whether the proposal has been executed
bool executed;
/// @notice The block at which this proposal was created
uint256 creationBlock;
}
}
// SPDX-License-Identifier: BSD-3-Clause
/// @title The Nouns DAO executor and treasury, supporting DAO fork
/*********************************
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░██░░░████░░██░░░████░░░ *
* ░░██████░░░████████░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
*********************************/
// LICENSE
// NounsDAOExecutor2.sol is a modified version of Compound Lab's Timelock.sol:
// https://github.com/compound-finance/compound-protocol/blob/20abad28055a2f91df48a90f8bb6009279a4cb35/contracts/Timelock.sol
//
// Timelock.sol source code Copyright 2020 Compound Labs, Inc. licensed under the BSD-3-Clause license.
// With modifications by Nounders DAO.
//
// Additional conditions of BSD-3-Clause can be found here: https://opensource.org/licenses/BSD-3-Clause
//
// MODIFICATIONS
// NounsDAOExecutor2.sol is a modified version of NounsDAOExecutor.sol
//
// NounsDAOExecutor.sol modifications:
// NounsDAOExecutor.sol modifies Timelock to use Solidity 0.8.x receive(), fallback(), and built-in over/underflow protection
// This contract acts as executor of Nouns DAO governance and its treasury, so it has been modified to accept ETH.
//
//
// NounsDAOExecutor2.sol modifications:
// - `sendETH` and `sendERC20` functions used for DAO forks
// - is upgradable via UUPSUpgradeable. uses intializer instead of constructor.
// - `GRACE_PERIOD` has been increased from 14 days to 21 days to allow more time in case of a forking period
pragma solidity ^0.8.19;
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import { Initializable } from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import { UUPSUpgradeable } from '@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol';
import { Address } from '@openzeppelin/contracts/utils/Address.sol';
contract NounsDAOExecutorV2 is UUPSUpgradeable, Initializable {
using SafeERC20 for IERC20;
using Address for address payable;
event NewAdmin(address indexed newAdmin);
event NewPendingAdmin(address indexed newPendingAdmin);
event NewDelay(uint256 indexed newDelay);
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event ETHSent(address indexed to, uint256 amount);
event ERC20Sent(address indexed to, address indexed erc20Token, uint256 amount);
string public constant NAME = 'NounsDAOExecutorV2';
/// @dev increased grace period from 14 days to 21 days to allow more time in case of a forking period
uint256 public constant GRACE_PERIOD = 21 days;
uint256 public constant MINIMUM_DELAY = 2 days;
uint256 public constant MAXIMUM_DELAY = 30 days;
address public admin;
address public pendingAdmin;
uint256 public delay;
mapping(bytes32 => bool) public queuedTransactions;
constructor() initializer {}
function initialize(address admin_, uint256 delay_) public virtual initializer {
require(delay_ >= MINIMUM_DELAY, 'NounsDAOExecutor::constructor: Delay must exceed minimum delay.');
require(delay_ <= MAXIMUM_DELAY, 'NounsDAOExecutor::setDelay: Delay must not exceed maximum delay.');
admin = admin_;
delay = delay_;
}
function setDelay(uint256 delay_) public {
require(msg.sender == address(this), 'NounsDAOExecutor::setDelay: Call must come from NounsDAOExecutor.');
require(delay_ >= MINIMUM_DELAY, 'NounsDAOExecutor::setDelay: Delay must exceed minimum delay.');
require(delay_ <= MAXIMUM_DELAY, 'NounsDAOExecutor::setDelay: Delay must not exceed maximum delay.');
delay = delay_;
emit NewDelay(delay_);
}
function acceptAdmin() public {
require(msg.sender == pendingAdmin, 'NounsDAOExecutor::acceptAdmin: Call must come from pendingAdmin.');
admin = msg.sender;
pendingAdmin = address(0);
emit NewAdmin(msg.sender);
}
function setPendingAdmin(address pendingAdmin_) public {
require(
msg.sender == address(this),
'NounsDAOExecutor::setPendingAdmin: Call must come from NounsDAOExecutor.'
);
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin_);
}
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public returns (bytes32) {
require(msg.sender == admin, 'NounsDAOExecutor::queueTransaction: Call must come from admin.');
require(
eta >= getBlockTimestamp() + delay,
'NounsDAOExecutor::queueTransaction: Estimated execution block must satisfy delay.'
);
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
return txHash;
}
function cancelTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public {
require(msg.sender == admin, 'NounsDAOExecutor::cancelTransaction: Call must come from admin.');
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
function executeTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public returns (bytes memory) {
require(msg.sender == admin, 'NounsDAOExecutor::executeTransaction: Call must come from admin.');
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
require(queuedTransactions[txHash], "NounsDAOExecutor::executeTransaction: Transaction hasn't been queued.");
require(
getBlockTimestamp() >= eta,
"NounsDAOExecutor::executeTransaction: Transaction hasn't surpassed time lock."
);
require(
getBlockTimestamp() <= eta + GRACE_PERIOD,
'NounsDAOExecutor::executeTransaction: Transaction is stale.'
);
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// solium-disable-next-line security/no-call-value
(bool success, bytes memory returnData) = target.call{ value: value }(callData);
require(success, 'NounsDAOExecutor::executeTransaction: Transaction execution reverted.');
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
function getBlockTimestamp() internal view returns (uint256) {
// solium-disable-next-line security/no-block-members
return block.timestamp;
}
receive() external payable {}
fallback() external payable {}
function sendETH(address payable recipient, uint256 ethToSend) external {
require(msg.sender == admin, 'NounsDAOExecutor::sendETH: Call must come from admin.');
recipient.sendValue(ethToSend);
emit ETHSent(recipient, ethToSend);
}
function sendERC20(
address recipient,
address erc20Token,
uint256 tokensToSend
) external {
require(msg.sender == admin, 'NounsDAOExecutor::sendERC20: Call must come from admin.');
IERC20(erc20Token).safeTransfer(recipient, tokensToSend);
emit ERC20Sent(recipient, erc20Token, tokensToSend);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address) internal view override {
require(
msg.sender == address(this),
'NounsDAOExecutor::_authorizeUpgrade: Call must come from NounsDAOExecutor.'
);
}
}
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.19;
interface INounsTokenForkLike {
function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);
function totalSupply() external view returns (uint256);
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function remainingTokensToClaim() external view returns (uint256);
function forkingPeriodEndTimestamp() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal initializer {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal initializer {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
import "../beacon/IBeacon.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
* @custom:oz-upgrades-unsafe-allow delegatecall
*/
abstract contract ERC1967Upgrade {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallSecure(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
address oldImplementation = _getImplementation();
// Initial upgrade and setup call
_setImplementation(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
// Perform rollback test if not already in progress
StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
if (!rollbackTesting.value) {
// Trigger rollback using upgradeTo from the new implementation
rollbackTesting.value = true;
Address.functionDelegateCall(
newImplementation,
abi.encodeWithSignature("upgradeTo(address)", oldImplementation)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_upgradeTo(newImplementation);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Emitted when the beacon is upgraded.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/utils/Initializable.sol)
pragma solidity ^0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
* initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() initializer {}
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}