Transaction Hash:
Block:
18588986 at Nov-17-2023 03:41:35 AM +UTC
Transaction Fee:
0.00243082508254256 ETH
$9.22
Gas Used:
103,840 Gas / 23.409332459 Gwei
Emitted Events:
217 |
DydxGovernor.VoteEmitted( id=16, voter=[Sender] 0xb85737d0d9c81e4cfaa6453374d08bea87e1c705, support=True, votingPower=5444409909657363168 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x7E9B1672...79A9018D2 | (dYdX: Governance) | ||||
0xB85737D0...a87e1c705 |
0.245578951550203614 Eth
Nonce: 669
|
0.243148126467661054 Eth
Nonce: 670
| 0.00243082508254256 | ||
0xDAFEA492...692c98Bc5
Miner
| (Flashbots: Builder) | 14.009175961343522468 Eth | 14.009186345343522468 Eth | 0.000010384 |
Execution Trace
DydxGovernor.submitVote( proposalId=16, support=True )
GovernanceStrategyV2.getVotingPowerAt( user=0xB85737D0D9C81E4cfAa6453374D08bea87e1c705, blockNumber=18572574 ) => ( 5444409909657363168 )
-
WrappedEthereumDydxToken.getPowerAtBlock( user=0xB85737D0D9C81E4cfAa6453374D08bea87e1c705, blockNumber=18572574, delegationType=0 ) => ( 5444409909657363168 )
InitializableAdminUpgradeabilityProxy.c2ffbb91( )
-
SafetyModuleV2.getPowerAtBlock( user=0xB85737D0D9C81E4cfAa6453374D08bea87e1c705, blockNumber=18572574, delegationType=0 ) => ( 0 )
-
-
DydxToken.getPowerAtBlock( user=0xB85737D0D9C81E4cfAa6453374D08bea87e1c705, blockNumber=18572574, delegationType=0 ) => ( 0 )
-
submitVote[DydxGovernor (ln:590)]
_submitVote[DydxGovernor (ln:591)]
getProposalState[DydxGovernor (ln:802)]
isProposalPassed[DydxGovernor (ln:768)]
isProposalOverGracePeriod[DydxGovernor (ln:774)]
getVotingPowerAt[DydxGovernor (ln:806)]
add[DydxGovernor (ln:811)]
add[DydxGovernor (ln:813)]
VoteEmitted[DydxGovernor (ln:817)]
File 1 of 6: DydxGovernor
File 2 of 6: GovernanceStrategyV2
File 3 of 6: WrappedEthereumDydxToken
File 4 of 6: InitializableAdminUpgradeabilityProxy
File 5 of 6: SafetyModuleV2
File 6 of 6: DydxToken
// SPDX-License-Identifier: MIT pragma solidity ^0.7.5; import "./Context.sol"; import "./Strings.sol"; import "./ERC165.sol"; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { function hasRole(bytes32 role, address account) external view returns (bool); function getRoleAdmin(bytes32 role) external view returns (bytes32); function grantRole(bytes32 role, address account) external; function revokeRole(bytes32 role, address account) external; function renounceRole(bytes32 role, address account) external; } /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/ */ function _checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); _roles[role].adminRole = adminRole; } function _grantRole(bytes32 role, address account) private { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } function _revokeRole(bytes32 role, address account) private { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev String operations. */ library Strings { bytes16 private constant alphabet = '0123456789abcdef'; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return '0'; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0x00'; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = '0'; buffer[1] = 'x'; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = alphabet[value & 0xf]; value >>= 4; } require(value == 0, 'Strings: hex length insufficient'); return string(buffer); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './IERC165.sol'; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // SPDX-License-Identifier: AGPL-3.0 // // Contracts by dYdX Foundation. Individual files are released under different licenses. // // https://dydx.community // https://github.com/dydxfoundation/governance-contracts pragma solidity 0.7.5; pragma abicoder v2; import { AccessControl } from '../dependencies/open-zeppelin/AccessControl.sol'; import { SafeMath } from '../dependencies/open-zeppelin/SafeMath.sol'; import { IDydxGovernor } from '../interfaces/IDydxGovernor.sol'; import { IExecutorWithTimelock } from '../interfaces/IExecutorWithTimelock.sol'; import { IGovernanceStrategy } from '../interfaces/IGovernanceStrategy.sol'; import { IProposalValidator } from '../interfaces/IProposalValidator.sol'; import { IVotingStrategy } from '../interfaces/IVotingStrategy.sol'; import { isContract, getChainId } from '../misc/Helpers.sol'; /** * @title dYdX governor contract. * @author dYdX * * @notice Main point of interaction for dYdX governance. Holds governance proposals. Delegates to * the governance strategy contract to determine how voting and proposing powers are counted. The * content of a proposal is a sequence of function calls. These function calls must be made * through authorized executor contracts. * * Functionality includes: * - Create a proposal * - Cancel a proposal * - Queue a proposal * - Execute a proposal * - Submit a vote to a proposal * * Proposal state transitions in success case: * * Pending => Active => Succeeded => Queued => Executed * * Proposal state transitions in failure cases: * * Pending => Active => Failed * Pending => Active => Succeeded => Queued => Expired * Pending => Canceled * Pending => Active => Canceled * Pending => Active => Succeeded => Canceled * Pending => Active => Succeeded => Queued => Canceled **/ contract DydxGovernor is AccessControl, IDydxGovernor { using SafeMath for uint256; // ============ Constants ============ bytes32 public constant OWNER_ROLE = keccak256('OWNER_ROLE'); bytes32 public constant ADD_EXECUTOR_ROLE = keccak256('ADD_EXECUTOR_ROLE'); // ============ Storage ============ address private _governanceStrategy; uint256 private _votingDelay; uint256 private _proposalsCount; mapping(uint256 => Proposal) private _proposals; mapping(address => bool) private _authorizedExecutors; bytes32 public constant DOMAIN_TYPEHASH = keccak256( 'EIP712Domain(string name,uint256 chainId,address verifyingContract)' ); bytes32 public constant VOTE_EMITTED_TYPEHASH = keccak256( 'VoteEmitted(uint256 id,bool support)' ); string public constant EIP712_DOMAIN_NAME = 'dYdX Governance'; constructor( address governanceStrategy, uint256 votingDelay, address addExecutorAdmin ) { _setGovernanceStrategy(governanceStrategy); _setVotingDelay(votingDelay); // Assign roles. _setupRole(OWNER_ROLE, msg.sender); _setupRole(ADD_EXECUTOR_ROLE, addExecutorAdmin); // Set OWNER_ROLE as the admin for all roles. _setRoleAdmin(OWNER_ROLE, OWNER_ROLE); _setRoleAdmin(ADD_EXECUTOR_ROLE, OWNER_ROLE); } struct CreateVars { uint256 startBlock; uint256 endBlock; uint256 previousProposalsCount; } /** * @notice Creates a Proposal (needs to be validated by the Proposal Validator) * @param executor The ExecutorWithTimelock contract that will execute the proposal * @param targets list of contracts called by proposal's associated transactions * @param values list of value in wei for each propoposal's associated transaction * @param signatures list of function signatures (can be empty) to be used when created the callData * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments * @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target * @param ipfsHash IPFS hash of the proposal **/ function create( IExecutorWithTimelock executor, address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas, bool[] memory withDelegatecalls, bytes32 ipfsHash ) external override returns (uint256) { require(targets.length != 0, 'INVALID_EMPTY_TARGETS'); require( targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length && targets.length == withDelegatecalls.length, 'INCONSISTENT_PARAMS_LENGTH' ); require(isExecutorAuthorized(address(executor)), 'EXECUTOR_NOT_AUTHORIZED'); require( IProposalValidator(address(executor)).validateCreatorOfProposal( this, msg.sender, block.number - 1 ), 'PROPOSITION_CREATION_INVALID' ); CreateVars memory vars; vars.startBlock = block.number.add(_votingDelay); vars.endBlock = vars.startBlock.add(IProposalValidator(address(executor)).VOTING_DURATION()); vars.previousProposalsCount = _proposalsCount; Proposal storage newProposal = _proposals[vars.previousProposalsCount]; newProposal.id = vars.previousProposalsCount; newProposal.creator = msg.sender; newProposal.executor = executor; newProposal.targets = targets; newProposal.values = values; newProposal.signatures = signatures; newProposal.calldatas = calldatas; newProposal.withDelegatecalls = withDelegatecalls; newProposal.startBlock = vars.startBlock; newProposal.endBlock = vars.endBlock; newProposal.strategy = _governanceStrategy; newProposal.ipfsHash = ipfsHash; _proposalsCount = vars.previousProposalsCount + 1; emit ProposalCreated( vars.previousProposalsCount, msg.sender, executor, targets, values, signatures, calldatas, withDelegatecalls, vars.startBlock, vars.endBlock, _governanceStrategy, ipfsHash ); return newProposal.id; } /** * @dev Cancels a Proposal. Callable by anyone if the conditions on the executor are fulfilled. * @param proposalId id of the proposal **/ function cancel(uint256 proposalId) external override { ProposalState state = getProposalState(proposalId); require( state != ProposalState.Canceled && state != ProposalState.Failed && state != ProposalState.Expired && state != ProposalState.Executed, 'ONLY_BEFORE_EXECUTED' ); Proposal storage proposal = _proposals[proposalId]; require( IProposalValidator(address(proposal.executor)).validateProposalCancellation( this, proposal.creator, block.number - 1 ), 'PROPOSITION_CANCELLATION_INVALID' ); proposal.canceled = true; for (uint256 i = 0; i < proposal.targets.length; i++) { proposal.executor.cancelTransaction( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.executionTime, proposal.withDelegatecalls[i] ); } emit ProposalCanceled(proposalId); } /** * @dev Queue the proposal. Requires that the proposal succeeded. * @param proposalId id of the proposal to queue **/ function queue(uint256 proposalId) external override { require(getProposalState(proposalId) == ProposalState.Succeeded, 'INVALID_STATE_FOR_QUEUE'); Proposal storage proposal = _proposals[proposalId]; uint256 executionTime = block.timestamp.add(proposal.executor.getDelay()); for (uint256 i = 0; i < proposal.targets.length; i++) { _queueOrRevert( proposal.executor, proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], executionTime, proposal.withDelegatecalls[i] ); } proposal.executionTime = executionTime; emit ProposalQueued(proposalId, executionTime, msg.sender); } /** * @dev Execute the proposal. Requires that the proposal is queued. * @param proposalId id of the proposal to execute **/ function execute(uint256 proposalId) external payable override { require(getProposalState(proposalId) == ProposalState.Queued, 'ONLY_QUEUED_PROPOSALS'); Proposal storage proposal = _proposals[proposalId]; proposal.executed = true; for (uint256 i = 0; i < proposal.targets.length; i++) { proposal.executor.executeTransaction{value: proposal.values[i]}( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.executionTime, proposal.withDelegatecalls[i] ); } emit ProposalExecuted(proposalId, msg.sender); } /** * @dev Function allowing msg.sender to vote for/against a proposal * @param proposalId id of the proposal * @param support boolean, true = vote for, false = vote against **/ function submitVote(uint256 proposalId, bool support) external override { return _submitVote(msg.sender, proposalId, support); } /** * @dev Function to register the vote of user that has voted offchain via signature * @param proposalId id of the proposal * @param support boolean, true = vote for, false = vote against * @param v v part of the voter signature * @param r r part of the voter signature * @param s s part of the voter signature **/ function submitVoteBySignature( uint256 proposalId, bool support, uint8 v, bytes32 r, bytes32 s ) external override { bytes32 digest = keccak256( abi.encodePacked( '\\x19\\x01', keccak256( abi.encode( DOMAIN_TYPEHASH, keccak256(bytes(EIP712_DOMAIN_NAME)), getChainId(), address(this) ) ), keccak256(abi.encode(VOTE_EMITTED_TYPEHASH, proposalId, support)) ) ); address signer = ecrecover(digest, v, r, s); require(signer != address(0), 'INVALID_SIGNATURE'); return _submitVote(signer, proposalId, support); } /** * @dev Set new GovernanceStrategy * Note: owner should be a timelocked executor, so needs to make a proposal * @param governanceStrategy new Address of the GovernanceStrategy contract **/ function setGovernanceStrategy(address governanceStrategy) external override onlyRole(OWNER_ROLE) { _setGovernanceStrategy(governanceStrategy); } /** * @dev Set new Voting Delay (delay before a newly created proposal can be voted on) * Note: owner should be a timelocked executor, so needs to make a proposal * @param votingDelay new voting delay in terms of blocks **/ function setVotingDelay(uint256 votingDelay) external override onlyRole(OWNER_ROLE) { _setVotingDelay(votingDelay); } /** * @dev Add new addresses to the list of authorized executors * @param executors list of new addresses to be authorized executors **/ function authorizeExecutors(address[] memory executors) public override onlyRole(ADD_EXECUTOR_ROLE) { for (uint256 i = 0; i < executors.length; i++) { _authorizeExecutor(executors[i]); } } /** * @dev Remove addresses to the list of authorized executors * @param executors list of addresses to be removed as authorized executors **/ function unauthorizeExecutors(address[] memory executors) public override onlyRole(OWNER_ROLE) { for (uint256 i = 0; i < executors.length; i++) { _unauthorizeExecutor(executors[i]); } } /** * @dev Getter of the current GovernanceStrategy address * @return The address of the current GovernanceStrategy contracts **/ function getGovernanceStrategy() external view override returns (address) { return _governanceStrategy; } /** * @dev Getter of the current Voting Delay (delay before a created proposal can be voted on) * Different from the voting duration * @return The voting delay in number of blocks **/ function getVotingDelay() external view override returns (uint256) { return _votingDelay; } /** * @dev Returns whether an address is an authorized executor * @param executor address to evaluate as authorized executor * @return true if authorized **/ function isExecutorAuthorized(address executor) public view override returns (bool) { return _authorizedExecutors[executor]; } /** * @dev Getter of the proposal count (the current number of proposals ever created) * @return the proposal count **/ function getProposalsCount() external view override returns (uint256) { return _proposalsCount; } /** * @dev Getter of a proposal by id * @param proposalId id of the proposal to get * @return the proposal as ProposalWithoutVotes memory object **/ function getProposalById(uint256 proposalId) external view override returns (ProposalWithoutVotes memory) { Proposal storage proposal = _proposals[proposalId]; ProposalWithoutVotes memory proposalWithoutVotes = ProposalWithoutVotes({ id: proposal.id, creator: proposal.creator, executor: proposal.executor, targets: proposal.targets, values: proposal.values, signatures: proposal.signatures, calldatas: proposal.calldatas, withDelegatecalls: proposal.withDelegatecalls, startBlock: proposal.startBlock, endBlock: proposal.endBlock, executionTime: proposal.executionTime, forVotes: proposal.forVotes, againstVotes: proposal.againstVotes, executed: proposal.executed, canceled: proposal.canceled, strategy: proposal.strategy, ipfsHash: proposal.ipfsHash }); return proposalWithoutVotes; } /** * @notice Get information about a voter's vote on a proposal. * Note: Vote is a struct: ({bool support, uint248 votingPower}) * @param proposalId id of the proposal * @param voter address of the voter * @return The associated Vote object **/ function getVoteOnProposal(uint256 proposalId, address voter) external view override returns (Vote memory) { return _proposals[proposalId].votes[voter]; } /** * @notice Get the current state of a proposal. * @param proposalId id of the proposal * @return The current state of the proposal **/ function getProposalState(uint256 proposalId) public view override returns (ProposalState) { require(_proposalsCount > proposalId, '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 (!IProposalValidator(address(proposal.executor)).isProposalPassed(this, proposalId)) { return ProposalState.Failed; } else if (proposal.executionTime == 0) { return ProposalState.Succeeded; } else if (proposal.executed) { return ProposalState.Executed; } else if (proposal.executor.isProposalOverGracePeriod(this, proposalId)) { return ProposalState.Expired; } else { return ProposalState.Queued; } } function _queueOrRevert( IExecutorWithTimelock executor, address target, uint256 value, string memory signature, bytes memory callData, uint256 executionTime, bool withDelegatecall ) internal { require( !executor.isActionQueued( keccak256(abi.encode(target, value, signature, callData, executionTime, withDelegatecall)) ), 'DUPLICATED_ACTION' ); executor.queueTransaction(target, value, signature, callData, executionTime, withDelegatecall); } function _submitVote( address voter, uint256 proposalId, bool support ) internal { require(getProposalState(proposalId) == ProposalState.Active, 'VOTING_CLOSED'); Proposal storage proposal = _proposals[proposalId]; Vote storage vote = proposal.votes[voter]; require(vote.votingPower == 0, 'VOTE_ALREADY_SUBMITTED'); uint256 votingPower = IVotingStrategy(proposal.strategy).getVotingPowerAt( voter, proposal.startBlock ); if (support) { proposal.forVotes = proposal.forVotes.add(votingPower); } else { proposal.againstVotes = proposal.againstVotes.add(votingPower); } vote.support = support; vote.votingPower = uint248(votingPower); emit VoteEmitted(proposalId, voter, support, votingPower); } function _setGovernanceStrategy(address governanceStrategy) internal { _governanceStrategy = governanceStrategy; emit GovernanceStrategyChanged(governanceStrategy, msg.sender); } function _setVotingDelay(uint256 votingDelay) internal { _votingDelay = votingDelay; emit VotingDelayChanged(votingDelay, msg.sender); } function _authorizeExecutor(address executor) internal { _authorizedExecutors[executor] = true; emit ExecutorAuthorized(executor); } function _unauthorizeExecutor(address executor) internal { _authorizedExecutors[executor] = false; emit ExecutorUnauthorized(executor); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { IExecutorWithTimelock } from './IExecutorWithTimelock.sol'; interface IDydxGovernor { enum ProposalState { Pending, Canceled, Active, Failed, Succeeded, Queued, Expired, Executed } struct Vote { bool support; uint248 votingPower; } struct Proposal { uint256 id; address creator; IExecutorWithTimelock executor; address[] targets; uint256[] values; string[] signatures; bytes[] calldatas; bool[] withDelegatecalls; uint256 startBlock; uint256 endBlock; uint256 executionTime; uint256 forVotes; uint256 againstVotes; bool executed; bool canceled; address strategy; bytes32 ipfsHash; mapping(address => Vote) votes; } struct ProposalWithoutVotes { uint256 id; address creator; IExecutorWithTimelock executor; address[] targets; uint256[] values; string[] signatures; bytes[] calldatas; bool[] withDelegatecalls; uint256 startBlock; uint256 endBlock; uint256 executionTime; uint256 forVotes; uint256 againstVotes; bool executed; bool canceled; address strategy; bytes32 ipfsHash; } /** * @dev emitted when a new proposal is created * @param id Id of the proposal * @param creator address of the creator * @param executor The ExecutorWithTimelock contract that will execute the proposal * @param targets list of contracts called by proposal's associated transactions * @param values list of value in wei for each propoposal's associated transaction * @param signatures list of function signatures (can be empty) to be used when created the callData * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments * @param withDelegatecalls boolean, true = transaction delegatecalls the taget, else calls the target * @param startBlock block number when vote starts * @param endBlock block number when vote ends * @param strategy address of the governanceStrategy contract * @param ipfsHash IPFS hash of the proposal **/ event ProposalCreated( uint256 id, address indexed creator, IExecutorWithTimelock indexed executor, address[] targets, uint256[] values, string[] signatures, bytes[] calldatas, bool[] withDelegatecalls, uint256 startBlock, uint256 endBlock, address strategy, bytes32 ipfsHash ); /** * @dev emitted when a proposal is canceled * @param id Id of the proposal **/ event ProposalCanceled(uint256 id); /** * @dev emitted when a proposal is queued * @param id Id of the proposal * @param executionTime time when proposal underlying transactions can be executed * @param initiatorQueueing address of the initiator of the queuing transaction **/ event ProposalQueued(uint256 id, uint256 executionTime, address indexed initiatorQueueing); /** * @dev emitted when a proposal is executed * @param id Id of the proposal * @param initiatorExecution address of the initiator of the execution transaction **/ event ProposalExecuted(uint256 id, address indexed initiatorExecution); /** * @dev emitted when a vote is registered * @param id Id of the proposal * @param voter address of the voter * @param support boolean, true = vote for, false = vote against * @param votingPower Power of the voter/vote **/ event VoteEmitted(uint256 id, address indexed voter, bool support, uint256 votingPower); event GovernanceStrategyChanged(address indexed newStrategy, address indexed initiatorChange); event VotingDelayChanged(uint256 newVotingDelay, address indexed initiatorChange); event ExecutorAuthorized(address executor); event ExecutorUnauthorized(address executor); /** * @dev Creates a Proposal (needs Proposition Power of creator > Threshold) * @param executor The ExecutorWithTimelock contract that will execute the proposal * @param targets list of contracts called by proposal's associated transactions * @param values list of value in wei for each propoposal's associated transaction * @param signatures list of function signatures (can be empty) to be used when created the callData * @param calldatas list of calldatas: if associated signature empty, calldata ready, else calldata is arguments * @param withDelegatecalls if true, transaction delegatecalls the taget, else calls the target * @param ipfsHash IPFS hash of the proposal **/ function create( IExecutorWithTimelock executor, address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas, bool[] memory withDelegatecalls, bytes32 ipfsHash ) external returns (uint256); /** * @dev Cancels a Proposal, when proposal is Pending/Active and threshold no longer reached * @param proposalId id of the proposal **/ function cancel(uint256 proposalId) external; /** * @dev Queue the proposal (If Proposal Succeeded) * @param proposalId id of the proposal to queue **/ function queue(uint256 proposalId) external; /** * @dev Execute the proposal (If Proposal Queued) * @param proposalId id of the proposal to execute **/ function execute(uint256 proposalId) external payable; /** * @dev Function allowing msg.sender to vote for/against a proposal * @param proposalId id of the proposal * @param support boolean, true = vote for, false = vote against **/ function submitVote(uint256 proposalId, bool support) external; /** * @dev Function to register the vote of user that has voted offchain via signature * @param proposalId id of the proposal * @param support boolean, true = vote for, false = vote against * @param v v part of the voter signature * @param r r part of the voter signature * @param s s part of the voter signature **/ function submitVoteBySignature( uint256 proposalId, bool support, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Set new GovernanceStrategy * Note: owner should be a timelocked executor, so needs to make a proposal * @param governanceStrategy new Address of the GovernanceStrategy contract **/ function setGovernanceStrategy(address governanceStrategy) external; /** * @dev Set new Voting Delay (delay before a newly created proposal can be voted on) * Note: owner should be a timelocked executor, so needs to make a proposal * @param votingDelay new voting delay in seconds **/ function setVotingDelay(uint256 votingDelay) external; /** * @dev Add new addresses to the list of authorized executors * @param executors list of new addresses to be authorized executors **/ function authorizeExecutors(address[] memory executors) external; /** * @dev Remove addresses to the list of authorized executors * @param executors list of addresses to be removed as authorized executors **/ function unauthorizeExecutors(address[] memory executors) external; /** * @dev Getter of the current GovernanceStrategy address * @return The address of the current GovernanceStrategy contracts **/ function getGovernanceStrategy() external view returns (address); /** * @dev Getter of the current Voting Delay (delay before a created proposal can be voted on) * Different from the voting duration * @return The voting delay in seconds **/ function getVotingDelay() external view returns (uint256); /** * @dev Returns whether an address is an authorized executor * @param executor address to evaluate as authorized executor * @return true if authorized **/ function isExecutorAuthorized(address executor) external view returns (bool); /** * @dev Getter of the proposal count (the current number of proposals ever created) * @return the proposal count **/ function getProposalsCount() external view returns (uint256); /** * @dev Getter of a proposal by id * @param proposalId id of the proposal to get * @return the proposal as ProposalWithoutVotes memory object **/ function getProposalById(uint256 proposalId) external view returns (ProposalWithoutVotes memory); /** * @dev Getter of the Vote of a voter about a proposal * Note: Vote is a struct: ({bool support, uint248 votingPower}) * @param proposalId id of the proposal * @param voter address of the voter * @return The associated Vote memory object **/ function getVoteOnProposal(uint256 proposalId, address voter) external view returns (Vote memory); /** * @dev Get the current state of a proposal * @param proposalId id of the proposal * @return The current state if the proposal **/ function getProposalState(uint256 proposalId) external view returns (ProposalState); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { IDydxGovernor } from './IDydxGovernor.sol'; interface IExecutorWithTimelock { /** * @dev emitted when a new pending admin is set * @param newPendingAdmin address of the new pending admin **/ event NewPendingAdmin(address newPendingAdmin); /** * @dev emitted when a new admin is set * @param newAdmin address of the new admin **/ event NewAdmin(address newAdmin); /** * @dev emitted when a new delay (between queueing and execution) is set * @param delay new delay **/ event NewDelay(uint256 delay); /** * @dev emitted when a new (trans)action is Queued. * @param actionHash hash of the action * @param target address of the targeted contract * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target **/ event QueuedAction( bytes32 actionHash, address indexed target, uint256 value, string signature, bytes data, uint256 executionTime, bool withDelegatecall ); /** * @dev emitted when an action is Cancelled * @param actionHash hash of the action * @param target address of the targeted contract * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target **/ event CancelledAction( bytes32 actionHash, address indexed target, uint256 value, string signature, bytes data, uint256 executionTime, bool withDelegatecall ); /** * @dev emitted when an action is Cancelled * @param actionHash hash of the action * @param target address of the targeted contract * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target * @param resultData the actual callData used on the target **/ event ExecutedAction( bytes32 actionHash, address indexed target, uint256 value, string signature, bytes data, uint256 executionTime, bool withDelegatecall, bytes resultData ); /** * @dev Getter of the current admin address (should be governance) * @return The address of the current admin **/ function getAdmin() external view returns (address); /** * @dev Getter of the current pending admin address * @return The address of the pending admin **/ function getPendingAdmin() external view returns (address); /** * @dev Getter of the delay between queuing and execution * @return The delay in seconds **/ function getDelay() external view returns (uint256); /** * @dev Returns whether an action (via actionHash) is queued * @param actionHash hash of the action to be checked * keccak256(abi.encode(target, value, signature, data, executionTime, withDelegatecall)) * @return true if underlying action of actionHash is queued **/ function isActionQueued(bytes32 actionHash) external view returns (bool); /** * @dev Checks whether a proposal is over its grace period * @param governance Governance contract * @param proposalId Id of the proposal against which to test * @return true of proposal is over grace period **/ function isProposalOverGracePeriod(IDydxGovernor governance, uint256 proposalId) external view returns (bool); /** * @dev Getter of grace period constant * @return grace period in seconds **/ function GRACE_PERIOD() external view returns (uint256); /** * @dev Getter of minimum delay constant * @return minimum delay in seconds **/ function MINIMUM_DELAY() external view returns (uint256); /** * @dev Getter of maximum delay constant * @return maximum delay in seconds **/ function MAXIMUM_DELAY() external view returns (uint256); /** * @dev Function, called by Governance, that queue a transaction, returns action hash * @param target smart contract target * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target **/ function queueTransaction( address target, uint256 value, string memory signature, bytes memory data, uint256 executionTime, bool withDelegatecall ) external returns (bytes32); /** * @dev Function, called by Governance, that executes a transaction, returns the callData executed * @param target smart contract target * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target **/ function executeTransaction( address target, uint256 value, string memory signature, bytes memory data, uint256 executionTime, bool withDelegatecall ) external payable returns (bytes memory); /** * @dev Function, called by Governance, that cancels a transaction, returns action hash * @param target smart contract target * @param value wei value of the transaction * @param signature function signature of the transaction * @param data function arguments of the transaction or callData if signature empty * @param executionTime time at which to execute the transaction * @param withDelegatecall boolean, true = transaction delegatecalls the target, else calls the target **/ function cancelTransaction( address target, uint256 value, string memory signature, bytes memory data, uint256 executionTime, bool withDelegatecall ) external returns (bytes32); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; interface IGovernanceStrategy { /** * @dev Returns the Proposition Power of a user at a specific block number. * @param user Address of the user. * @param blockNumber Blocknumber at which to fetch Proposition Power * @return Power number **/ function getPropositionPowerAt(address user, uint256 blockNumber) external view returns (uint256); /** * @dev Returns the total supply of Outstanding Proposition Tokens * @param blockNumber Blocknumber at which to evaluate * @return total supply at blockNumber **/ function getTotalPropositionSupplyAt(uint256 blockNumber) external view returns (uint256); /** * @dev Returns the total supply of Outstanding Voting Tokens * @param blockNumber Blocknumber at which to evaluate * @return total supply at blockNumber **/ function getTotalVotingSupplyAt(uint256 blockNumber) external view returns (uint256); /** * @dev Returns the Vote Power of a user at a specific block number. * @param user Address of the user. * @param blockNumber Blocknumber at which to fetch Vote Power * @return Vote number **/ function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { IDydxGovernor } from './IDydxGovernor.sol'; interface IProposalValidator { /** * @dev Called to validate a proposal (e.g when creating new proposal in Governance) * @param governance Governance Contract * @param user Address of the proposal creator * @param blockNumber Block Number against which to make the test (e.g proposal creation block -1). * @return boolean, true if can be created **/ function validateCreatorOfProposal( IDydxGovernor governance, address user, uint256 blockNumber ) external view returns (bool); /** * @dev Called to validate the cancellation of a proposal * @param governance Governance Contract * @param user Address of the proposal creator * @param blockNumber Block Number against which to make the test (e.g proposal creation block -1). * @return boolean, true if can be cancelled **/ function validateProposalCancellation( IDydxGovernor governance, address user, uint256 blockNumber ) external view returns (bool); /** * @dev Returns whether a user has enough Proposition Power to make a proposal. * @param governance Governance Contract * @param user Address of the user to be challenged. * @param blockNumber Block Number against which to make the challenge. * @return true if user has enough power **/ function isPropositionPowerEnough( IDydxGovernor governance, address user, uint256 blockNumber ) external view returns (bool); /** * @dev Returns the minimum Proposition Power needed to create a proposition. * @param governance Governance Contract * @param blockNumber Blocknumber at which to evaluate * @return minimum Proposition Power needed **/ function getMinimumPropositionPowerNeeded(IDydxGovernor governance, uint256 blockNumber) external view returns (uint256); /** * @dev Returns whether a proposal passed or not * @param governance Governance Contract * @param proposalId Id of the proposal to set * @return true if proposal passed **/ function isProposalPassed(IDydxGovernor governance, uint256 proposalId) external view returns (bool); /** * @dev Check whether a proposal has reached quorum, ie has enough FOR-voting-power * Here quorum is not to understand as number of votes reached, but number of for-votes reached * @param governance Governance Contract * @param proposalId Id of the proposal to verify * @return voting power needed for a proposal to pass **/ function isQuorumValid(IDydxGovernor governance, uint256 proposalId) external view returns (bool); /** * @dev Check whether a proposal has enough extra FOR-votes than AGAINST-votes * FOR VOTES - AGAINST VOTES > VOTE_DIFFERENTIAL * voting supply * @param governance Governance Contract * @param proposalId Id of the proposal to verify * @return true if enough For-Votes **/ function isVoteDifferentialValid(IDydxGovernor governance, uint256 proposalId) external view returns (bool); /** * @dev Calculates the minimum amount of Voting Power needed for a proposal to Pass * @param votingSupply Total number of oustanding voting tokens * @return voting power needed for a proposal to pass **/ function getMinimumVotingPowerNeeded(uint256 votingSupply) external view returns (uint256); /** * @dev Get proposition threshold constant value * @return the proposition threshold value (100 <=> 1%) **/ function PROPOSITION_THRESHOLD() external view returns (uint256); /** * @dev Get voting duration constant value * @return the voting duration value in seconds **/ function VOTING_DURATION() external view returns (uint256); /** * @dev Get the vote differential threshold constant value * to compare with % of for votes/total supply - % of against votes/total supply * @return the vote differential threshold value (100 <=> 1%) **/ function VOTE_DIFFERENTIAL() external view returns (uint256); /** * @dev Get quorum threshold constant value * to compare with % of for votes/total supply * @return the quorum threshold value (100 <=> 1%) **/ function MINIMUM_QUORUM() external view returns (uint256); /** * @dev precision helper: 100% = 10000 * @return one hundred percents with our chosen precision **/ function ONE_HUNDRED_WITH_PRECISION() external view returns (uint256); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; interface IVotingStrategy { function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; function getChainId() pure returns (uint256) { uint256 chainId; assembly { chainId := chainid() } return chainId; } function isContract(address account) view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); }
File 2 of 6: GovernanceStrategyV2
// SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.5; import "../../interfaces/IERC20.sol"; import "./Context.sol"; import "./SafeMath.sol"; import "./Address.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string internal _name; string internal _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() virtual public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() virtual public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() virtual public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { IGovernancePowerDelegationERC20 } from '../../interfaces/IGovernancePowerDelegationERC20.sol'; import { IGovernanceStrategy } from '../../interfaces/IGovernanceStrategy.sol'; import { GovernancePowerDelegationERC20Mixin } from '../token/GovernancePowerDelegationERC20Mixin.sol'; interface IDydxToken { function _totalSupplySnapshots( uint256 ) external view returns (GovernancePowerDelegationERC20Mixin.Snapshot memory); function _totalSupplySnapshotsCount() external view returns (uint256); } /** * @title GovernanceStrategyV2 * @author dYdX * * @notice Smart contract containing logic to measure users' relative governance power for creating * and voting on proposals. * * User Power = User Power from each of: DYDX, stkDYDX, wethDYDX. * User Power from Token = Token Power + Token Power as Delegatee [- Token Power if user has delegated] * Two wrapper functions linked to DYDX tokens's GovernancePowerDelegationERC20Mixin.sol implementation * - getPropositionPowerAt: fetching a user Proposition Power at a specified block * - getVotingPowerAt: fetching a user Voting Power at a specified block */ contract GovernanceStrategyV2 is IGovernanceStrategy { // ============ Constants ============ /// @notice The DYDX governance token. address public immutable DYDX_TOKEN; /// @notice Token representing staked positions of the DYDX token. address public immutable STAKED_DYDX_TOKEN; /// @notice Token representing Wrapped Ethereum DYDX tokens. address public immutable WRAPPED_ETHEREUM_DYDX_TOKEN; // ============ Constructor ============ constructor( address dydxToken, address stakedDydxToken, address wrappedEthereumDydxToken ) { DYDX_TOKEN = dydxToken; STAKED_DYDX_TOKEN = stakedDydxToken; WRAPPED_ETHEREUM_DYDX_TOKEN = wrappedEthereumDydxToken; } // ============ Other Functions ============ /** * @notice Get the total supply of proposition power, for the purpose of determining if a * proposing threshold was reached. * * @param blockNumber Block number at which to evaluate. * * @return The total proposition power supply at the given block number. */ function getTotalPropositionSupplyAt( uint256 blockNumber ) public view override returns (uint256) { return _getTotalSupplyAt(blockNumber); } /** * @notice Get the total supply of voting power, for the purpose of determining if quorum or vote * differential tresholds were reached. * * @param blockNumber Block number at which to evaluate. * * @return The total voting power supply at the given block number. */ function getTotalVotingSupplyAt( uint256 blockNumber ) public view override returns (uint256) { return _getTotalSupplyAt(blockNumber); } /** * @notice The proposition power of an address at a given block number. * * @param user Address to check. * @param blockNumber Block number at which to evaluate. * * @return The proposition power of the address at the given block number. */ function getPropositionPowerAt( address user, uint256 blockNumber ) public view override returns (uint256) { return _getPowerByTypeAt( user, blockNumber, IGovernancePowerDelegationERC20.DelegationType.PROPOSITION_POWER ); } /** * @notice The voting power of an address at a given block number. * * @param user Address of the user. * @param blockNumber Block number at which to evaluate. * * @return The voting power of the address at the given block number. */ function getVotingPowerAt( address user, uint256 blockNumber ) public view override returns (uint256) { return _getPowerByTypeAt( user, blockNumber, IGovernancePowerDelegationERC20.DelegationType.VOTING_POWER ); } function _getPowerByTypeAt( address user, uint256 blockNumber, IGovernancePowerDelegationERC20.DelegationType powerType ) internal view returns (uint256) { return ( IGovernancePowerDelegationERC20(DYDX_TOKEN).getPowerAtBlock( user, blockNumber, powerType ) + IGovernancePowerDelegationERC20(STAKED_DYDX_TOKEN).getPowerAtBlock( user, blockNumber, powerType ) + IGovernancePowerDelegationERC20(WRAPPED_ETHEREUM_DYDX_TOKEN).getPowerAtBlock( user, blockNumber, powerType ) ); } /** * @dev The total supply of the DYDX token at a given block number. * * @param blockNumber Block number at which to evaluate. * * @return The total DYDX token supply at the given block number. */ function _getTotalSupplyAt( uint256 blockNumber ) internal view returns (uint256) { IDydxToken dydxToken = IDydxToken(DYDX_TOKEN); uint256 snapshotsCount = dydxToken._totalSupplySnapshotsCount(); // Iterate in reverse over the total supply snapshots, up to index 1. for (uint256 i = snapshotsCount - 1; i != 0; i--) { GovernancePowerDelegationERC20Mixin.Snapshot memory snapshot = ( dydxToken._totalSupplySnapshots(i) ); if (snapshot.blockNumber <= blockNumber) { return snapshot.value; } } // If blockNumber was on or after the first snapshot, then return the initial supply. // Else, blockNumber is before token launch so return 0. GovernancePowerDelegationERC20Mixin.Snapshot memory firstSnapshot = ( dydxToken._totalSupplySnapshots(0) ); if (firstSnapshot.blockNumber <= blockNumber) { return firstSnapshot.value; } else { return 0; } } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; import { ERC20 } from '../../dependencies/open-zeppelin/ERC20.sol'; import { SafeMath } from '../../dependencies/open-zeppelin/SafeMath.sol'; import { IGovernancePowerDelegationERC20 } from '../../interfaces/IGovernancePowerDelegationERC20.sol'; /** * @title GovernancePowerDelegationERC20Mixin * @author dYdX * * @notice Provides support for two types of governance powers, both endowed by the governance * token, and separately delegatable. Provides functions for delegation and for querying a user's * power at a certain block number. */ abstract contract GovernancePowerDelegationERC20Mixin is ERC20, IGovernancePowerDelegationERC20 { using SafeMath for uint256; // ============ Constants ============ /// @notice EIP-712 typehash for delegation by signature of a specific governance power type. bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256( 'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)' ); /// @notice EIP-712 typehash for delegation by signature of all governance powers. bytes32 public constant DELEGATE_TYPEHASH = keccak256( 'Delegate(address delegatee,uint256 nonce,uint256 expiry)' ); // ============ Structs ============ /// @dev Snapshot of a value on a specific block, used to track voting power for proposals. struct Snapshot { uint128 blockNumber; uint128 value; } // ============ External Functions ============ /** * @notice Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external override { _delegateByType(msg.sender, delegatee, delegationType); } /** * @notice Delegates all governance powers to a delegatee. * * @param delegatee The address to delegate power to. */ function delegate(address delegatee) external override { _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER); _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER); } /** * @notice Returns the delegatee of a user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external override view returns (address) { (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); return _getDelegatee(delegator, delegates); } /** * @notice Returns the current power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number); } /** * @notice Returns the power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber); } // ============ Internal Functions ============ /** * @dev Delegates one specific power to a delegatee. * * @param delegator The user whose power to delegate. * @param delegatee The address to delegate power to. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _delegateByType( address delegator, address delegatee, DelegationType delegationType ) internal { require(delegatee != address(0), 'INVALID_DELEGATEE'); (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); uint256 delegatorBalance = balanceOf(delegator); address previousDelegatee = _getDelegatee(delegator, delegates); delegates[delegator] = delegatee; _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType); emit DelegateChanged(delegator, delegatee, delegationType); } /** * @dev Moves power from one user to another. * * @param from The user from which delegated power is moved. * @param to The user that will receive the delegated power. * @param amount The amount of power to be moved. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _moveDelegatesByType( address from, address to, uint256 amount, DelegationType delegationType ) internal { if (from == to) { return; } ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); if (from != address(0)) { uint256 previous = 0; uint256 fromSnapshotsCount = snapshotsCounts[from]; if (fromSnapshotsCount != 0) { previous = snapshots[from][fromSnapshotsCount - 1].value; } else { previous = balanceOf(from); } uint256 newAmount = previous.sub(amount); _writeSnapshot( snapshots, snapshotsCounts, from, uint128(newAmount) ); emit DelegatedPowerChanged(from, newAmount, delegationType); } if (to != address(0)) { uint256 previous = 0; uint256 toSnapshotsCount = snapshotsCounts[to]; if (toSnapshotsCount != 0) { previous = snapshots[to][toSnapshotsCount - 1].value; } else { previous = balanceOf(to); } uint256 newAmount = previous.add(amount); _writeSnapshot( snapshots, snapshotsCounts, to, uint128(newAmount) ); emit DelegatedPowerChanged(to, newAmount, delegationType); } } /** * @dev Searches for a balance snapshot by block number using binary search. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param user The user for which the snapshot is being searched. * @param blockNumber The block number being searched. */ function _searchByBlockNumber( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address user, uint256 blockNumber ) internal view returns (uint256) { require(blockNumber <= block.number, 'INVALID_BLOCK_NUMBER'); uint256 snapshotsCount = snapshotsCounts[user]; if (snapshotsCount == 0) { return balanceOf(user); } // First check most recent balance if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) { return snapshots[user][snapshotsCount - 1].value; } // Next check implicit zero balance if (snapshots[user][0].blockNumber > blockNumber) { return 0; } uint256 lower = 0; uint256 upper = snapshotsCount - 1; while (upper > lower) { uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Snapshot memory snapshot = snapshots[user][center]; if (snapshot.blockNumber == blockNumber) { return snapshot.value; } else if (snapshot.blockNumber < blockNumber) { lower = center; } else { upper = center - 1; } } return snapshots[user][lower].value; } /** * @dev Returns delegation data (snapshot, snapshotsCount, delegates) by delegation type. * * Note: This mixin contract does not itself define any storage, and we require the inheriting * contract to implement this method to provide access to the relevant mappings in storage. * This pattern was implemented by Aave for legacy reasons and we have decided not to change it. * * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _getDelegationDataByType(DelegationType delegationType) internal virtual view returns ( mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots mapping(address => uint256) storage, // snapshotsCount mapping(address => address) storage // delegates ); /** * @dev Writes a snapshot of a user's token/power balance. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param owner The user whose power to snapshot. * @param newValue The new balance to snapshot at the current block. */ function _writeSnapshot( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address owner, uint128 newValue ) internal { uint128 currentBlock = uint128(block.number); uint256 ownerSnapshotsCount = snapshotsCounts[owner]; mapping(uint256 => Snapshot) storage ownerSnapshots = snapshots[owner]; if ( ownerSnapshotsCount != 0 && ownerSnapshots[ownerSnapshotsCount - 1].blockNumber == currentBlock ) { // Doing multiple operations in the same block ownerSnapshots[ownerSnapshotsCount - 1].value = newValue; } else { ownerSnapshots[ownerSnapshotsCount] = Snapshot(currentBlock, newValue); snapshotsCounts[owner] = ownerSnapshotsCount + 1; } } /** * @dev Returns the delegatee of a user. If a user never performed any delegation, their * delegated address will be 0x0, in which case we return the user's own address. * * @param delegator The address of the user for which return the delegatee. * @param delegates The mapping of delegates for a particular type of delegation. */ function _getDelegatee( address delegator, mapping(address => address) storage delegates ) internal view returns (address) { address previousDelegatee = delegates[delegator]; if (previousDelegatee == address(0)) { return delegator; } return previousDelegatee; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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: AGPL-3.0 pragma solidity 0.7.5; interface IGovernancePowerDelegationERC20 { enum DelegationType { VOTING_POWER, PROPOSITION_POWER } /** * @dev Emitted when a user delegates governance power to another user. * * @param delegator The delegator. * @param delegatee The delegatee. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegateChanged( address indexed delegator, address indexed delegatee, DelegationType delegationType ); /** * @dev Emitted when an action changes the delegated power of a user. * * @param user The user whose delegated power has changed. * @param amount The new amount of delegated power for the user. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType); /** * @dev Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external virtual; /** * @dev Delegates all governance powers to a delegatee. * * @param delegatee The user to which the power will be delegated. */ function delegate(address delegatee) external virtual; /** * @dev Returns the delegatee of an user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external view virtual returns (address); /** * @dev Returns the current delegated power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external view virtual returns (uint256); /** * @dev Returns the delegated power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external view virtual returns (uint256); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; interface IGovernanceStrategy { /** * @dev Returns the Proposition Power of a user at a specific block number. * @param user Address of the user. * @param blockNumber Blocknumber at which to fetch Proposition Power * @return Power number **/ function getPropositionPowerAt(address user, uint256 blockNumber) external view returns (uint256); /** * @dev Returns the total supply of Outstanding Proposition Tokens * @param blockNumber Blocknumber at which to evaluate * @return total supply at blockNumber **/ function getTotalPropositionSupplyAt(uint256 blockNumber) external view returns (uint256); /** * @dev Returns the total supply of Outstanding Voting Tokens * @param blockNumber Blocknumber at which to evaluate * @return total supply at blockNumber **/ function getTotalVotingSupplyAt(uint256 blockNumber) external view returns (uint256); /** * @dev Returns the Vote Power of a user at a specific block number. * @param user Address of the user. * @param blockNumber Blocknumber at which to fetch Vote Power * @return Vote number **/ function getVotingPowerAt(address user, uint256 blockNumber) external view returns (uint256); }
File 3 of 6: WrappedEthereumDydxToken
// SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.5; import "../../interfaces/IERC20.sol"; import "./Context.sol"; import "./SafeMath.sol"; import "./Address.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string internal _name; string internal _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() virtual public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() virtual public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() virtual public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import { IERC20 } from '../../interfaces/IERC20.sol'; import { SafeMath } from './SafeMath.sol'; import { Address } from './Address.sol'; /** * @title SafeERC20 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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 SafeMath for uint256; 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)); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { 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 callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), 'SafeERC20: call to non-contract'); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, 'SafeERC20: low-level call failed'); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed'); } } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Interface of a bridge contract. */ interface IBridge { /** * @dev Emitted when a bridge event occurs. * * @param id Unique ID of the bridge event. * @param amount Amount of tokens bridged. * @param from The Ethereum address the tokens were transferred from. * @param accAddress The address to send to. * @param data Any arbitrary data. */ event Bridge( uint256 indexed id, uint256 amount, address from, bytes accAddress, bytes data ); /** * @notice Bridge a token. * * @param amount The amount of tokens to bridge * @param accAddress The address to send to. * @param memo Arbitrary memo to include in the event. */ function bridge( uint256 amount, bytes calldata accAddress, bytes calldata memo ) external; } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; import { IBridge } from './IBridge.sol'; import { ERC20 } from '../../dependencies/open-zeppelin/ERC20.sol'; import { SafeERC20 } from '../../dependencies/open-zeppelin/SafeERC20.sol'; import { SafeMath } from '../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../interfaces/IERC20.sol'; import { GovernancePowerDelegationERC20Mixin } from '../token/GovernancePowerDelegationERC20Mixin.sol'; /** * @title WrappedEthereumDydxToken * @author dYdX * * @notice The Wrapped Ethereum DYDX governance token. */ contract WrappedEthereumDydxToken is GovernancePowerDelegationERC20Mixin, IBridge { using SafeERC20 for IERC20; using SafeMath for uint256; // ============ Constants ============ string internal constant NAME = 'Wrapped Ethereum DYDX'; string internal constant SYMBOL = 'wethDYDX'; bytes32 public immutable DOMAIN_SEPARATOR; bytes public constant EIP712_VERSION = '1'; bytes32 public constant EIP712_DOMAIN = keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ); bytes32 public constant PERMIT_TYPEHASH = keccak256( 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)' ); IERC20 public immutable DYDX_TOKEN; // ============ Storage ============ /// @dev Mapping from (owner) => (next valid nonce) for EIP-712 signatures. mapping(address => uint256) internal _nonces; mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots; mapping(address => uint256) public _votingSnapshotsCounts; mapping(address => address) public _votingDelegates; mapping(address => mapping(uint256 => Snapshot)) public _propositionPowerSnapshots; mapping(address => uint256) public _propositionPowerSnapshotsCounts; mapping(address => address) public _propositionPowerDelegates; /// @notice The next available (unused) id for the bridge event. Equal to the number of events. uint256 public _nextAvailableBridgeId; // ============ Constructor ============ /** * @notice Constructor. * @param tokenAddress The address of the token to bridge. */ constructor( ERC20 tokenAddress ) ERC20(NAME, SYMBOL) { uint256 chainId; // solium-disable-next-line assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, keccak256(bytes(NAME)), keccak256(bytes(EIP712_VERSION)), chainId, address(this) ) ); DYDX_TOKEN = tokenAddress; } /** * @notice Bridge the DYDX token and receive wethDYDX. * * @param amount The amount of tokens to bridge * @param accAddress The address to send to. * @param memo Arbitrary memo to include in the event. For possible future compatibility. */ function bridge( uint256 amount, bytes calldata accAddress, bytes calldata memo ) external override { // Wrap the tokens. DYDX_TOKEN.safeTransferFrom(msg.sender, address(this), amount); _mint(msg.sender, amount); // Emit the event and increase the nonce. uint256 nonce = _nextAvailableBridgeId; emit Bridge( nonce, amount, msg.sender, accAddress, memo ); _nextAvailableBridgeId = nonce + 1; } /** * @notice Implements the permit function as specified in EIP-2612. * * @param owner Address of the token owner. * @param spender Address of the spender. * @param value Amount of allowance. * @param deadline Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(owner != address(0), 'INVALID_OWNER'); require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); uint256 currentValidNonce = _nonces[owner]; bytes32 digest = keccak256( abi.encodePacked( '\\x19\\x01', DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) ) ); require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); _nonces[owner] = currentValidNonce.add(1); _approve(owner, spender, value); } /** * @notice Get the next valid nonce for EIP-712 signatures. * * This nonce should be used when signing for any of the following functions: * - permit() * - delegateByTypeBySig() * - delegateBySig() */ function nonces(address owner) external view returns (uint256) { return _nonces[owner]; } /** * @dev Writes a snapshot before any transfer operation, including: _transfer, _mint and _burn. * - On _transfer, it writes snapshots for both 'from' and 'to'. * - On _mint, only for `to`. * - On _burn, only for `from`. * * @param from The sender. * @param to The recipient. * @param amount The amount being transfered. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal override { address votingFromDelegatee = _getDelegatee(from, _votingDelegates); address votingToDelegatee = _getDelegatee(to, _votingDelegates); _moveDelegatesByType( votingFromDelegatee, votingToDelegatee, amount, DelegationType.VOTING_POWER ); address propPowerFromDelegatee = _getDelegatee(from, _propositionPowerDelegates); address propPowerToDelegatee = _getDelegatee(to, _propositionPowerDelegates); _moveDelegatesByType( propPowerFromDelegatee, propPowerToDelegatee, amount, DelegationType.PROPOSITION_POWER ); } function _getDelegationDataByType(DelegationType delegationType) internal override view returns ( mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots mapping(address => uint256) storage, // snapshots count mapping(address => address) storage // delegatees list ) { if (delegationType == DelegationType.VOTING_POWER) { return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates); } else { return ( _propositionPowerSnapshots, _propositionPowerSnapshotsCounts, _propositionPowerDelegates ); } } /** * @dev Delegates specific governance power from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateByTypeBySig( address delegatee, DelegationType delegationType, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) public { bytes32 structHash = keccak256( abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry) ); bytes32 digest = keccak256(abi.encodePacked('\\x19\\x01', DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(digest, v, r, s); require(signer != address(0), 'INVALID_SIGNATURE'); require(nonce == _nonces[signer]++, 'INVALID_NONCE'); require(block.timestamp <= expiry, 'INVALID_EXPIRATION'); _delegateByType(signer, delegatee, delegationType); } /** * @dev Delegates both governance powers from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateBySig( address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) public { bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked('\\x19\\x01', DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(digest, v, r, s); require(signer != address(0), 'INVALID_SIGNATURE'); require(nonce == _nonces[signer]++, 'INVALID_NONCE'); require(block.timestamp <= expiry, 'INVALID_EXPIRATION'); _delegateByType(signer, delegatee, DelegationType.VOTING_POWER); _delegateByType(signer, delegatee, DelegationType.PROPOSITION_POWER); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; import { ERC20 } from '../../dependencies/open-zeppelin/ERC20.sol'; import { SafeMath } from '../../dependencies/open-zeppelin/SafeMath.sol'; import { IGovernancePowerDelegationERC20 } from '../../interfaces/IGovernancePowerDelegationERC20.sol'; /** * @title GovernancePowerDelegationERC20Mixin * @author dYdX * * @notice Provides support for two types of governance powers, both endowed by the governance * token, and separately delegatable. Provides functions for delegation and for querying a user's * power at a certain block number. */ abstract contract GovernancePowerDelegationERC20Mixin is ERC20, IGovernancePowerDelegationERC20 { using SafeMath for uint256; // ============ Constants ============ /// @notice EIP-712 typehash for delegation by signature of a specific governance power type. bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256( 'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)' ); /// @notice EIP-712 typehash for delegation by signature of all governance powers. bytes32 public constant DELEGATE_TYPEHASH = keccak256( 'Delegate(address delegatee,uint256 nonce,uint256 expiry)' ); // ============ Structs ============ /// @dev Snapshot of a value on a specific block, used to track voting power for proposals. struct Snapshot { uint128 blockNumber; uint128 value; } // ============ External Functions ============ /** * @notice Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external override { _delegateByType(msg.sender, delegatee, delegationType); } /** * @notice Delegates all governance powers to a delegatee. * * @param delegatee The address to delegate power to. */ function delegate(address delegatee) external override { _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER); _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER); } /** * @notice Returns the delegatee of a user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external override view returns (address) { (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); return _getDelegatee(delegator, delegates); } /** * @notice Returns the current power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number); } /** * @notice Returns the power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber); } // ============ Internal Functions ============ /** * @dev Delegates one specific power to a delegatee. * * @param delegator The user whose power to delegate. * @param delegatee The address to delegate power to. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _delegateByType( address delegator, address delegatee, DelegationType delegationType ) internal { require(delegatee != address(0), 'INVALID_DELEGATEE'); (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); uint256 delegatorBalance = balanceOf(delegator); address previousDelegatee = _getDelegatee(delegator, delegates); delegates[delegator] = delegatee; _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType); emit DelegateChanged(delegator, delegatee, delegationType); } /** * @dev Moves power from one user to another. * * @param from The user from which delegated power is moved. * @param to The user that will receive the delegated power. * @param amount The amount of power to be moved. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _moveDelegatesByType( address from, address to, uint256 amount, DelegationType delegationType ) internal { if (from == to) { return; } ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); if (from != address(0)) { uint256 previous = 0; uint256 fromSnapshotsCount = snapshotsCounts[from]; if (fromSnapshotsCount != 0) { previous = snapshots[from][fromSnapshotsCount - 1].value; } else { previous = balanceOf(from); } uint256 newAmount = previous.sub(amount); _writeSnapshot( snapshots, snapshotsCounts, from, uint128(newAmount) ); emit DelegatedPowerChanged(from, newAmount, delegationType); } if (to != address(0)) { uint256 previous = 0; uint256 toSnapshotsCount = snapshotsCounts[to]; if (toSnapshotsCount != 0) { previous = snapshots[to][toSnapshotsCount - 1].value; } else { previous = balanceOf(to); } uint256 newAmount = previous.add(amount); _writeSnapshot( snapshots, snapshotsCounts, to, uint128(newAmount) ); emit DelegatedPowerChanged(to, newAmount, delegationType); } } /** * @dev Searches for a balance snapshot by block number using binary search. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param user The user for which the snapshot is being searched. * @param blockNumber The block number being searched. */ function _searchByBlockNumber( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address user, uint256 blockNumber ) internal view returns (uint256) { require(blockNumber <= block.number, 'INVALID_BLOCK_NUMBER'); uint256 snapshotsCount = snapshotsCounts[user]; if (snapshotsCount == 0) { return balanceOf(user); } // First check most recent balance if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) { return snapshots[user][snapshotsCount - 1].value; } // Next check implicit zero balance if (snapshots[user][0].blockNumber > blockNumber) { return 0; } uint256 lower = 0; uint256 upper = snapshotsCount - 1; while (upper > lower) { uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Snapshot memory snapshot = snapshots[user][center]; if (snapshot.blockNumber == blockNumber) { return snapshot.value; } else if (snapshot.blockNumber < blockNumber) { lower = center; } else { upper = center - 1; } } return snapshots[user][lower].value; } /** * @dev Returns delegation data (snapshot, snapshotsCount, delegates) by delegation type. * * Note: This mixin contract does not itself define any storage, and we require the inheriting * contract to implement this method to provide access to the relevant mappings in storage. * This pattern was implemented by Aave for legacy reasons and we have decided not to change it. * * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _getDelegationDataByType(DelegationType delegationType) internal virtual view returns ( mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots mapping(address => uint256) storage, // snapshotsCount mapping(address => address) storage // delegates ); /** * @dev Writes a snapshot of a user's token/power balance. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param owner The user whose power to snapshot. * @param newValue The new balance to snapshot at the current block. */ function _writeSnapshot( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address owner, uint128 newValue ) internal { uint128 currentBlock = uint128(block.number); uint256 ownerSnapshotsCount = snapshotsCounts[owner]; mapping(uint256 => Snapshot) storage ownerSnapshots = snapshots[owner]; if ( ownerSnapshotsCount != 0 && ownerSnapshots[ownerSnapshotsCount - 1].blockNumber == currentBlock ) { // Doing multiple operations in the same block ownerSnapshots[ownerSnapshotsCount - 1].value = newValue; } else { ownerSnapshots[ownerSnapshotsCount] = Snapshot(currentBlock, newValue); snapshotsCounts[owner] = ownerSnapshotsCount + 1; } } /** * @dev Returns the delegatee of a user. If a user never performed any delegation, their * delegated address will be 0x0, in which case we return the user's own address. * * @param delegator The address of the user for which return the delegatee. * @param delegates The mapping of delegates for a particular type of delegation. */ function _getDelegatee( address delegator, mapping(address => address) storage delegates ) internal view returns (address) { address previousDelegatee = delegates[delegator]; if (previousDelegatee == address(0)) { return delegator; } return previousDelegatee; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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: AGPL-3.0 pragma solidity 0.7.5; interface IGovernancePowerDelegationERC20 { enum DelegationType { VOTING_POWER, PROPOSITION_POWER } /** * @dev Emitted when a user delegates governance power to another user. * * @param delegator The delegator. * @param delegatee The delegatee. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegateChanged( address indexed delegator, address indexed delegatee, DelegationType delegationType ); /** * @dev Emitted when an action changes the delegated power of a user. * * @param user The user whose delegated power has changed. * @param amount The new amount of delegated power for the user. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType); /** * @dev Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external virtual; /** * @dev Delegates all governance powers to a delegatee. * * @param delegatee The user to which the power will be delegated. */ function delegate(address delegatee) external virtual; /** * @dev Returns the delegatee of an user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external view virtual returns (address); /** * @dev Returns the current delegated power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external view virtual returns (uint256); /** * @dev Returns the delegated power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external view virtual returns (uint256); }
File 4 of 6: InitializableAdminUpgradeabilityProxy
// SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './Proxy.sol'; import './Address.sol'; /** * @title BaseUpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract BaseUpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address indexed implementation); /** * @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 Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal override view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) internal { require( Address.isContract(newImplementation), 'Cannot set a proxy implementation to a non-contract address' ); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ abstract contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ fallback() external payable { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal virtual view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal virtual {} /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; import './BaseUpgradeabilityProxy.sol'; /** * @title InitializableUpgradeabilityProxy * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing * implementation and init data. */ contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Contract initializer. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ function initialize(address _logic, bytes memory _data) public payable { require(_implementation() == address(0)); assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success); } } } // SPDX-License-Identifier: AGPL-3.0 // // Contracts by dYdX Foundation. Individual files are released under different licenses. // // https://dydx.community // https://github.com/dydxfoundation/governance-contracts pragma solidity 0.7.5; import './BaseAdminUpgradeabilityProxy.sol'; import './InitializableUpgradeabilityProxy.sol'; /** * @title InitializableAdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for * initializing the implementation, admin, and init data. */ contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy { /** * Contract initializer. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ function initialize( address _logic, address _admin, bytes memory _data ) public payable { require(_implementation() == address(0)); InitializableUpgradeabilityProxy.initialize(_logic, _data); assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { BaseAdminUpgradeabilityProxy._willFallback(); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './UpgradeabilityProxy.sol'; /** * @title BaseAdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @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 Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @return The address of the proxy admin. */ function admin() external ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), 'Cannot change the admin of a proxy to the zero address'); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { _upgradeTo(newImplementation); (bool success, ) = newImplementation.delegatecall(data); require(success); } /** * @return adm The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal virtual override { require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin'); super._willFallback(); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './BaseUpgradeabilityProxy.sol'; /** * @title UpgradeabilityProxy * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing * implementation and init data. */ contract UpgradeabilityProxy is BaseUpgradeabilityProxy { /** * @dev Contract constructor. * @param _logic Address of the initial implementation. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if (_data.length > 0) { (bool success, ) = _logic.delegatecall(_data); require(success); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.5; import './BaseAdminUpgradeabilityProxy.sol'; /** * @title AdminUpgradeabilityProxy * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for * initializing the implementation, admin, and init data. */ contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { /** * Contract constructor. * @param _logic address of the initial implementation. * @param _admin Address of the proxy administrator. * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. * It should include the signature and the parameters of the function to be called, as described in * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. */ constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable { assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin'); super._willFallback(); } }
File 5 of 6: SafetyModuleV2
// Contracts by dYdX Foundation. Individual files are released under different licenses. // // https://dydx.community // https://github.com/dydxfoundation/governance-contracts // // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeERC20 } from '../../dependencies/open-zeppelin/SafeERC20.sol'; import { IERC20 } from '../../interfaces/IERC20.sol'; import { SM1Admin } from '../v1_1/impl/SM1Admin.sol'; import { SM1Getters } from '../v1_1/impl/SM1Getters.sol'; import { SM1Operators } from '../v1_1/impl/SM1Operators.sol'; import { SM1Slashing } from '../v1_1/impl/SM1Slashing.sol'; import { SM1Staking } from '../v1_1/impl/SM1Staking.sol'; /** * @title SafetyModuleV2 * @author dYdX * * @notice Contract for staking tokens, which may be slashed by the permissioned slasher. * * NOTE: Most functions will revert if epoch zero has not started. */ contract SafetyModuleV2 is SM1Slashing, SM1Operators, SM1Admin, SM1Getters { using SafeERC20 for IERC20; // ============ Constants ============ string public constant EIP712_DOMAIN_NAME = 'dYdX Safety Module'; string public constant EIP712_DOMAIN_VERSION = '1'; bytes32 public constant EIP712_DOMAIN_SCHEMA_HASH = keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ); // ============ Constructor ============ constructor( IERC20 stakedToken, IERC20 rewardsToken, address rewardsTreasury, uint256 distributionStart, uint256 distributionEnd ) SM1Staking(stakedToken, rewardsToken, rewardsTreasury, distributionStart, distributionEnd) {} // ============ External Functions ============ /** * @notice Initializer for v2, intended to fix the deployment bug that affected v1. * * Responsible for the following: * * 1. Funds recovery: * - Transfer all Safety Module DYDX to the rewards treasury. * * 2. Storage recovery and cleanup: * - Set the _EXCHANGE_RATE_ to EXCHANGE_RATE_BASE. * - Clean up invalid storage values at slots 115 and 125. */ function initialize() external initializer { // Funds recovery and staker compensation. uint256 balance = STAKED_TOKEN.balanceOf(address(this)); STAKED_TOKEN.safeTransfer(REWARDS_TREASURY, balance); // Storage recovery and cleanup. __SM1ExchangeRate_init(); // solhint-disable-next-line no-inline-assembly assembly { sstore(115, 0) sstore(125, 0) } } // ============ Internal Functions ============ /** * @dev Returns the revision of the implementation contract. * * @return The revision number. */ function getRevision() internal pure override returns (uint256) { return 2; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import { IERC20 } from '../../interfaces/IERC20.sol'; import { SafeMath } from './SafeMath.sol'; import { Address } from './Address.sol'; /** * @title SafeERC20 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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 SafeMath for uint256; 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)); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { 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 callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), 'SafeERC20: call to non-contract'); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, 'SafeERC20: low-level call failed'); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed'); } } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Roles } from './SM1Roles.sol'; import { SM1StakedBalances } from './SM1StakedBalances.sol'; /** * @title SM1Admin * @author dYdX * * @dev Admin-only functions. */ abstract contract SM1Admin is SM1StakedBalances, SM1Roles { using SafeMath for uint256; // ============ External Functions ============ /** * @notice Set the parameters defining the function from timestamp to epoch number. * * The formula used is `n = floor((t - b) / a)` where: * - `n` is the epoch number * - `t` is the timestamp (in seconds) * - `b` is a non-negative offset, indicating the start of epoch zero (in seconds) * - `a` is the length of an epoch, a.k.a. the interval (in seconds) * * Reverts if epoch zero already started, and the new parameters would change the current epoch. * Reverts if epoch zero has not started, but would have had started under the new parameters. * * @param interval The length `a` of an epoch, in seconds. * @param offset The offset `b`, i.e. the start of epoch zero, in seconds. */ function setEpochParameters( uint256 interval, uint256 offset ) external onlyRole(EPOCH_PARAMETERS_ROLE) nonReentrant { if (!hasEpochZeroStarted()) { require( block.timestamp < offset, 'SM1Admin: Started epoch zero' ); _setEpochParameters(interval, offset); return; } // We must settle the total active balance to ensure the index is recorded at the epoch // boundary as needed, before we make any changes to the epoch formula. _settleTotalActiveBalance(); // Update the epoch parameters. Require that the current epoch number is unchanged. uint256 originalCurrentEpoch = getCurrentEpoch(); _setEpochParameters(interval, offset); uint256 newCurrentEpoch = getCurrentEpoch(); require( originalCurrentEpoch == newCurrentEpoch, 'SM1Admin: Changed epochs' ); } /** * @notice Set the blackout window, during which one cannot request withdrawals of staked funds. */ function setBlackoutWindow( uint256 blackoutWindow ) external onlyRole(EPOCH_PARAMETERS_ROLE) nonReentrant { _setBlackoutWindow(blackoutWindow); } /** * @notice Set the emission rate of rewards. * * @param emissionPerSecond The new number of rewards tokens given out per second. */ function setRewardsPerSecond( uint256 emissionPerSecond ) external onlyRole(REWARDS_RATE_ROLE) nonReentrant { uint256 totalStaked = 0; if (hasEpochZeroStarted()) { // We must settle the total active balance to ensure the index is recorded at the epoch // boundary as needed, before we make any changes to the emission rate. totalStaked = _settleTotalActiveBalance(); } _setRewardsPerSecond(emissionPerSecond, totalStaked); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { Math } from '../../../utils/Math.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1Getters * @author dYdX * * @dev Some external getter functions. */ abstract contract SM1Getters is SM1Storage { using SafeMath for uint256; // ============ External Functions ============ /** * @notice The parameters specifying the function from timestamp to epoch number. * * @return The parameters struct with `interval` and `offset` fields. */ function getEpochParameters() external view returns (SM1Types.EpochParameters memory) { return _EPOCH_PARAMETERS_; } /** * @notice The period of time at the end of each epoch in which withdrawals cannot be requested. * * @return The blackout window duration, in seconds. */ function getBlackoutWindow() external view returns (uint256) { return _BLACKOUT_WINDOW_; } /** * @notice Get the domain separator used for EIP-712 signatures. * * @return The EIP-712 domain separator. */ function getDomainSeparator() external view returns (bytes32) { return _DOMAIN_SEPARATOR_; } /** * @notice The value of one underlying token, in the units used for staked balances, denominated * as a mutiple of EXCHANGE_RATE_BASE for additional precision. * * To convert from an underlying amount to a staked amount, multiply by the exchange rate. * * @return The exchange rate. */ function getExchangeRate() external view returns (uint256) { return _EXCHANGE_RATE_; } /** * @notice Get an exchange rate snapshot. * * @param index The index number of the exchange rate snapshot. * * @return The snapshot struct with `blockNumber` and `value` fields. */ function getExchangeRateSnapshot( uint256 index ) external view returns (SM1Types.Snapshot memory) { return _EXCHANGE_RATE_SNAPSHOTS_[index]; } /** * @notice Get the number of exchange rate snapshots. * * @return The number of snapshots that have been taken of the exchange rate. */ function getExchangeRateSnapshotCount() external view returns (uint256) { return _EXCHANGE_RATE_SNAPSHOT_COUNT_; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { SM1Roles } from './SM1Roles.sol'; import { SM1Staking } from './SM1Staking.sol'; /** * @title SM1Operators * @author dYdX * * @dev Actions which may be called by authorized operators, nominated by the contract owner. * * There are two types of operators. These should be smart contracts, which can be used to * provide additional functionality to users: * * STAKE_OPERATOR_ROLE: * * This operator is allowed to request withdrawals and withdraw funds on behalf of stakers. This * role could be used by a smart contract to provide a staking interface with additional * features, for example, optional lock-up periods that pay out additional rewards (from a * separate rewards pool). * * CLAIM_OPERATOR_ROLE: * * This operator is allowed to claim rewards on behalf of stakers. This role could be used by a * smart contract to provide an interface for claiming rewards from multiple incentive programs * at once. */ abstract contract SM1Operators is SM1Staking, SM1Roles { using SafeMath for uint256; // ============ Events ============ event OperatorStakedFor( address indexed staker, uint256 amount, address operator ); event OperatorWithdrawalRequestedFor( address indexed staker, uint256 amount, address operator ); event OperatorWithdrewStakeFor( address indexed staker, address recipient, uint256 amount, address operator ); event OperatorClaimedRewardsFor( address indexed staker, address recipient, uint256 claimedRewards, address operator ); // ============ External Functions ============ /** * @notice Request a withdrawal on behalf of a staker. * * Reverts if we are currently in the blackout window. * * @param staker The staker whose stake to request a withdrawal for. * @param stakeAmount The amount of stake to move from the active to the inactive balance. */ function requestWithdrawalFor( address staker, uint256 stakeAmount ) external onlyRole(STAKE_OPERATOR_ROLE) nonReentrant { _requestWithdrawal(staker, stakeAmount); emit OperatorWithdrawalRequestedFor(staker, stakeAmount, msg.sender); } /** * @notice Withdraw a staker's stake, and send to the specified recipient. * * @param staker The staker whose stake to withdraw. * @param recipient The address that should receive the funds. * @param stakeAmount The amount of stake to withdraw from the staker's inactive balance. */ function withdrawStakeFor( address staker, address recipient, uint256 stakeAmount ) external onlyRole(STAKE_OPERATOR_ROLE) nonReentrant { _withdrawStake(staker, recipient, stakeAmount); emit OperatorWithdrewStakeFor(staker, recipient, stakeAmount, msg.sender); } /** * @notice Claim rewards on behalf of a staker, and send them to the specified recipient. * * @param staker The staker whose rewards to claim. * @param recipient The address that should receive the funds. * * @return The number of rewards tokens claimed. */ function claimRewardsFor( address staker, address recipient ) external onlyRole(CLAIM_OPERATOR_ROLE) nonReentrant returns (uint256) { uint256 rewards = _settleAndClaimRewards(staker, recipient); // Emits an event internally. emit OperatorClaimedRewardsFor(staker, recipient, rewards, msg.sender); return rewards; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol'; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { Math } from '../../../utils/Math.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Roles } from './SM1Roles.sol'; import { SM1Staking } from './SM1Staking.sol'; /** * @title SM1Slashing * @author dYdX * * @dev Provides the slashing function for removing funds from the contract. * * SLASHING: * * All funds in the contract, active or inactive, are slashable. Slashes are recorded by updating * the exchange rate, and to simplify the technical implementation, we disallow full slashes. * To reduce the possibility of overflow in the exchange rate, we place an upper bound on the * fraction of funds that may be slashed in a single slash. * * Warning: Slashing is not possible if the slash would cause the exchange rate to overflow. * * REWARDS AND GOVERNANCE POWER ACCOUNTING: * * Since all slashes are accounted for by a global exchange rate, slashes do not require any * update to staked balances. The earning of rewards is unaffected by slashes. * * Governance power takes slashes into account by using snapshots of the exchange rate inside * the getPowerAtBlock() function. Note that getPowerAtBlock() returns the governance power as of * the end of the specified block. */ abstract contract SM1Slashing is SM1Staking, SM1Roles { using SafeERC20 for IERC20; using SafeMath for uint256; // ============ Constants ============ /// @notice The maximum fraction of funds that may be slashed in a single slash (numerator). uint256 public constant MAX_SLASH_NUMERATOR = 95; /// @notice The maximum fraction of funds that may be slashed in a single slash (denominator). uint256 public constant MAX_SLASH_DENOMINATOR = 100; // ============ Events ============ event Slashed( uint256 amount, address recipient, uint256 newExchangeRate ); // ============ External Functions ============ /** * @notice Slash staked token balances and withdraw those funds to the specified address. * * @param requestedSlashAmount The request slash amount, denominated in the underlying token. * @param recipient The address to receive the slashed tokens. * * @return The amount slashed, denominated in the underlying token. */ function slash( uint256 requestedSlashAmount, address recipient ) external onlyRole(SLASHER_ROLE) nonReentrant returns (uint256) { uint256 underlyingBalance = STAKED_TOKEN.balanceOf(address(this)); if (underlyingBalance == 0) { return 0; } // Get the slash amount and remaining amount. Note that remainingAfterSlash is nonzero. uint256 maxSlashAmount = underlyingBalance.mul(MAX_SLASH_NUMERATOR).div(MAX_SLASH_DENOMINATOR); uint256 slashAmount = Math.min(requestedSlashAmount, maxSlashAmount); uint256 remainingAfterSlash = underlyingBalance.sub(slashAmount); if (slashAmount == 0) { return 0; } // Update the exchange rate. // // Warning: Can revert if the max exchange rate is exceeded. uint256 newExchangeRate = updateExchangeRate(underlyingBalance, remainingAfterSlash); // Transfer the slashed token. STAKED_TOKEN.safeTransfer(recipient, slashAmount); emit Slashed(slashAmount, recipient, newExchangeRate); return slashAmount; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol'; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { Math } from '../../../utils/Math.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1ERC20 } from './SM1ERC20.sol'; import { SM1StakedBalances } from './SM1StakedBalances.sol'; /** * @title SM1Staking * @author dYdX * * @dev External functions for stakers. See SM1StakedBalances for details on staker accounting. * * UNDERLYING AND STAKED AMOUNTS: * * We distinguish between underlying amounts and stake amounts. An underlying amount is denoted * in the original units of the token being staked. A stake amount is adjusted by the exchange * rate, which can increase due to slashing. Before any slashes have occurred, the exchange rate * is equal to one. */ abstract contract SM1Staking is SM1StakedBalances, SM1ERC20 { using SafeERC20 for IERC20; using SafeMath for uint256; // ============ Events ============ event Staked( address indexed staker, address spender, uint256 underlyingAmount, uint256 stakeAmount ); event WithdrawalRequested( address indexed staker, uint256 stakeAmount ); event WithdrewStake( address indexed staker, address recipient, uint256 underlyingAmount, uint256 stakeAmount ); // ============ Constants ============ IERC20 public immutable STAKED_TOKEN; // ============ Constructor ============ constructor( IERC20 stakedToken, IERC20 rewardsToken, address rewardsTreasury, uint256 distributionStart, uint256 distributionEnd ) SM1StakedBalances(rewardsToken, rewardsTreasury, distributionStart, distributionEnd) { STAKED_TOKEN = stakedToken; } // ============ External Functions ============ /** * @notice Deposit and stake funds. These funds are active and start earning rewards immediately. * * @param underlyingAmount The amount of underlying token to stake. */ function stake( uint256 underlyingAmount ) external nonReentrant { _stake(msg.sender, underlyingAmount); } /** * @notice Deposit and stake on behalf of another address. * * @param staker The staker who will receive the stake. * @param underlyingAmount The amount of underlying token to stake. */ function stakeFor( address staker, uint256 underlyingAmount ) external nonReentrant { _stake(staker, underlyingAmount); } /** * @notice Request to withdraw funds. Starting in the next epoch, the funds will be “inactive” * and available for withdrawal. Inactive funds do not earn rewards. * * Reverts if we are currently in the blackout window. * * @param stakeAmount The amount of stake to move from the active to the inactive balance. */ function requestWithdrawal( uint256 stakeAmount ) external nonReentrant { _requestWithdrawal(msg.sender, stakeAmount); } /** * @notice Withdraw the sender's inactive funds, and send to the specified recipient. * * @param recipient The address that should receive the funds. * @param stakeAmount The amount of stake to withdraw from the sender's inactive balance. */ function withdrawStake( address recipient, uint256 stakeAmount ) external nonReentrant { _withdrawStake(msg.sender, recipient, stakeAmount); } /** * @notice Withdraw the max available inactive funds, and send to the specified recipient. * * This is less gas-efficient than querying the max via eth_call and calling withdrawStake(). * * @param recipient The address that should receive the funds. * * @return The withdrawn amount. */ function withdrawMaxStake( address recipient ) external nonReentrant returns (uint256) { uint256 stakeAmount = getStakeAvailableToWithdraw(msg.sender); _withdrawStake(msg.sender, recipient, stakeAmount); return stakeAmount; } /** * @notice Settle and claim all rewards, and send them to the specified recipient. * * Call this function with eth_call to query the claimable rewards balance. * * @param recipient The address that should receive the funds. * * @return The number of rewards tokens claimed. */ function claimRewards( address recipient ) external nonReentrant returns (uint256) { return _settleAndClaimRewards(msg.sender, recipient); // Emits an event internally. } // ============ Public Functions ============ /** * @notice Get the amount of stake available for a given staker to withdraw. * * @param staker The address whose balance to check. * * @return The staker's stake amount that is inactive and available to withdraw. */ function getStakeAvailableToWithdraw( address staker ) public view returns (uint256) { // Note that the next epoch inactive balance is always at least that of the current epoch. return getInactiveBalanceCurrentEpoch(staker); } // ============ Internal Functions ============ function _stake( address staker, uint256 underlyingAmount ) internal { // Convert using the exchange rate. uint256 stakeAmount = stakeAmountFromUnderlyingAmount(underlyingAmount); // Update staked balances and delegate snapshots. _increaseCurrentAndNextActiveBalance(staker, stakeAmount); _moveDelegatesForTransfer(address(0), staker, stakeAmount); // Transfer token from the sender. STAKED_TOKEN.safeTransferFrom(msg.sender, address(this), underlyingAmount); emit Staked(staker, msg.sender, underlyingAmount, stakeAmount); emit Transfer(address(0), msg.sender, stakeAmount); } function _requestWithdrawal( address staker, uint256 stakeAmount ) internal { require( !inBlackoutWindow(), 'SM1Staking: Withdraw requests restricted in the blackout window' ); // Get the staker's requestable amount and revert if there is not enough to request withdrawal. uint256 requestableBalance = getActiveBalanceNextEpoch(staker); require( stakeAmount <= requestableBalance, 'SM1Staking: Withdraw request exceeds next active balance' ); // Move amount from active to inactive in the next epoch. _moveNextBalanceActiveToInactive(staker, stakeAmount); emit WithdrawalRequested(staker, stakeAmount); } function _withdrawStake( address staker, address recipient, uint256 stakeAmount ) internal { // Get staker withdrawable balance and revert if there is not enough to withdraw. uint256 withdrawableBalance = getInactiveBalanceCurrentEpoch(staker); require( stakeAmount <= withdrawableBalance, 'SM1Staking: Withdraw amount exceeds staker inactive balance' ); // Update staked balances and delegate snapshots. _decreaseCurrentAndNextInactiveBalance(staker, stakeAmount); _moveDelegatesForTransfer(staker, address(0), stakeAmount); // Convert using the exchange rate. uint256 underlyingAmount = underlyingAmountFromStakeAmount(stakeAmount); // Transfer token to the recipient. STAKED_TOKEN.safeTransfer(recipient, underlyingAmount); emit Transfer(msg.sender, address(0), stakeAmount); emit WithdrewStake(staker, recipient, underlyingAmount, stakeAmount); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; library SM1Types { /** * @dev The parameters used to convert a timestamp to an epoch number. */ struct EpochParameters { uint128 interval; uint128 offset; } /** * @dev Snapshot of a value at a specific block, used to track historical governance power. */ struct Snapshot { uint256 blockNumber; uint256 value; } /** * @dev A balance, possibly with a change scheduled for the next epoch. * * @param currentEpoch The epoch in which the balance was last updated. * @param currentEpochBalance The balance at epoch `currentEpoch`. * @param nextEpochBalance The balance at epoch `currentEpoch + 1`. */ struct StoredBalance { uint16 currentEpoch; uint240 currentEpochBalance; uint240 nextEpochBalance; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1Roles * @author dYdX * * @dev Defines roles used in the SafetyModuleV1 contract. The hierarchy of roles and powers * of each role are described below. * * Roles: * * OWNER_ROLE * | -> May add or remove addresses from any of the roles below. * | * +-- SLASHER_ROLE * | -> Can slash staked token balances and withdraw those funds. * | * +-- EPOCH_PARAMETERS_ROLE * | -> May set epoch parameters such as the interval, offset, and blackout window. * | * +-- REWARDS_RATE_ROLE * | -> May set the emission rate of rewards. * | * +-- CLAIM_OPERATOR_ROLE * | -> May claim rewards on behalf of a user. * | * +-- STAKE_OPERATOR_ROLE * -> May manipulate user's staked funds (e.g. perform withdrawals on behalf of a user). */ abstract contract SM1Roles is SM1Storage { bytes32 public constant OWNER_ROLE = keccak256('OWNER_ROLE'); bytes32 public constant SLASHER_ROLE = keccak256('SLASHER_ROLE'); bytes32 public constant EPOCH_PARAMETERS_ROLE = keccak256('EPOCH_PARAMETERS_ROLE'); bytes32 public constant REWARDS_RATE_ROLE = keccak256('REWARDS_RATE_ROLE'); bytes32 public constant CLAIM_OPERATOR_ROLE = keccak256('CLAIM_OPERATOR_ROLE'); bytes32 public constant STAKE_OPERATOR_ROLE = keccak256('STAKE_OPERATOR_ROLE'); function __SM1Roles_init() internal { // Assign roles to the sender. // // The STAKE_OPERATOR_ROLE and CLAIM_OPERATOR_ROLE roles are not initially assigned. // These can be assigned to other smart contracts to provide additional functionality for users. _setupRole(OWNER_ROLE, msg.sender); _setupRole(SLASHER_ROLE, msg.sender); _setupRole(EPOCH_PARAMETERS_ROLE, msg.sender); _setupRole(REWARDS_RATE_ROLE, msg.sender); // Set OWNER_ROLE as the admin of all roles. _setRoleAdmin(OWNER_ROLE, OWNER_ROLE); _setRoleAdmin(SLASHER_ROLE, OWNER_ROLE); _setRoleAdmin(EPOCH_PARAMETERS_ROLE, OWNER_ROLE); _setRoleAdmin(REWARDS_RATE_ROLE, OWNER_ROLE); _setRoleAdmin(CLAIM_OPERATOR_ROLE, OWNER_ROLE); _setRoleAdmin(STAKE_OPERATOR_ROLE, OWNER_ROLE); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { SafeCast } from '../lib/SafeCast.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Rewards } from './SM1Rewards.sol'; /** * @title SM1StakedBalances * @author dYdX * * @dev Accounting of staked balances. * * NOTE: Functions may revert if epoch zero has not started. * * NOTE: All amounts dealt with in this file are denominated in staked units, which because of the * exchange rate, may not correspond one-to-one with the underlying token. See SM1Staking.sol. * * STAKED BALANCE ACCOUNTING: * * A staked balance is in one of two states: * - active: Earning staking rewards; cannot be withdrawn by staker; may be slashed. * - inactive: Not earning rewards; can be withdrawn by the staker; may be slashed. * * A staker may have a combination of active and inactive balances. The following operations * affect staked balances as follows: * - deposit: Increase active balance. * - request withdrawal: At the end of the current epoch, move some active funds to inactive. * - withdraw: Decrease inactive balance. * - transfer: Move some active funds to another staker. * * To encode the fact that a balance may be scheduled to change at the end of a certain epoch, we * store each balance as a struct of three fields: currentEpoch, currentEpochBalance, and * nextEpochBalance. * * REWARDS ACCOUNTING: * * Active funds earn rewards for the period of time that they remain active. This means, after * requesting a withdrawal of some funds, those funds will continue to earn rewards until the end * of the epoch. For example: * * epoch: n n + 1 n + 2 n + 3 * | | | | * +----------+----------+----------+-----... * ^ t_0: User makes a deposit. * ^ t_1: User requests a withdrawal of all funds. * ^ t_2: The funds change state from active to inactive. * * In the above scenario, the user would earn rewards for the period from t_0 to t_2, varying * with the total staked balance in that period. If the user only request a withdrawal for a part * of their balance, then the remaining balance would continue earning rewards beyond t_2. * * User rewards must be settled via SM1Rewards any time a user's active balance changes. Special * attention is paid to the the epoch boundaries, where funds may have transitioned from active * to inactive. * * SETTLEMENT DETAILS: * * Internally, this module uses the following types of operations on stored balances: * - Load: Loads a balance, while applying settlement logic internally to get the * up-to-date result. Returns settlement results without updating state. * - Store: Stores a balance. * - Load-for-update: Performs a load and applies updates as needed to rewards accounting. * Since this is state-changing, it must be followed by a store operation. * - Settle: Performs load-for-update and store operations. * * This module is responsible for maintaining the following invariants to ensure rewards are * calculated correctly: * - When an active balance is loaded for update, if a rollover occurs from one epoch to the * next, the rewards index must be settled up to the boundary at which the rollover occurs. * - Because the global rewards index is needed to update the user rewards index, the total * active balance must be settled before any staker balances are settled or loaded for update. * - A staker's balance must be settled before their rewards are settled. */ abstract contract SM1StakedBalances is SM1Rewards { using SafeCast for uint256; using SafeMath for uint256; // ============ Constructor ============ constructor( IERC20 rewardsToken, address rewardsTreasury, uint256 distributionStart, uint256 distributionEnd ) SM1Rewards(rewardsToken, rewardsTreasury, distributionStart, distributionEnd) {} // ============ Public Functions ============ /** * @notice Get the current active balance of a staker. */ function getActiveBalanceCurrentEpoch( address staker ) public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance( _ACTIVE_BALANCES_[staker] ); return uint256(balance.currentEpochBalance); } /** * @notice Get the next epoch active balance of a staker. */ function getActiveBalanceNextEpoch( address staker ) public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance( _ACTIVE_BALANCES_[staker] ); return uint256(balance.nextEpochBalance); } /** * @notice Get the current total active balance. */ function getTotalActiveBalanceCurrentEpoch() public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance( _TOTAL_ACTIVE_BALANCE_ ); return uint256(balance.currentEpochBalance); } /** * @notice Get the next epoch total active balance. */ function getTotalActiveBalanceNextEpoch() public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } (SM1Types.StoredBalance memory balance, , , ) = _loadActiveBalance( _TOTAL_ACTIVE_BALANCE_ ); return uint256(balance.nextEpochBalance); } /** * @notice Get the current inactive balance of a staker. * @dev The balance is converted via the index to token units. */ function getInactiveBalanceCurrentEpoch( address staker ) public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } SM1Types.StoredBalance memory balance = _loadInactiveBalance(_INACTIVE_BALANCES_[staker]); return uint256(balance.currentEpochBalance); } /** * @notice Get the next epoch inactive balance of a staker. * @dev The balance is converted via the index to token units. */ function getInactiveBalanceNextEpoch( address staker ) public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } SM1Types.StoredBalance memory balance = _loadInactiveBalance(_INACTIVE_BALANCES_[staker]); return uint256(balance.nextEpochBalance); } /** * @notice Get the current total inactive balance. */ function getTotalInactiveBalanceCurrentEpoch() public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } SM1Types.StoredBalance memory balance = _loadInactiveBalance(_TOTAL_INACTIVE_BALANCE_); return uint256(balance.currentEpochBalance); } /** * @notice Get the next epoch total inactive balance. */ function getTotalInactiveBalanceNextEpoch() public view returns (uint256) { if (!hasEpochZeroStarted()) { return 0; } SM1Types.StoredBalance memory balance = _loadInactiveBalance(_TOTAL_INACTIVE_BALANCE_); return uint256(balance.nextEpochBalance); } /** * @notice Get the current transferable balance for a user. The user can * only transfer their balance that is not currently inactive or going to be * inactive in the next epoch. Note that this means the user's transferable funds * are their active balance of the next epoch. * * @param account The account to get the transferable balance of. * * @return The user's transferable balance. */ function getTransferableBalance( address account ) public view returns (uint256) { return getActiveBalanceNextEpoch(account); } // ============ Internal Functions ============ function _increaseCurrentAndNextActiveBalance( address staker, uint256 amount ) internal { // Always settle total active balance before settling a staker active balance. uint256 oldTotalBalance = _increaseCurrentAndNextBalances(address(0), true, amount); uint256 oldUserBalance = _increaseCurrentAndNextBalances(staker, true, amount); // When an active balance changes at current timestamp, settle rewards to the current timestamp. _settleUserRewardsUpToNow(staker, oldUserBalance, oldTotalBalance); } function _moveNextBalanceActiveToInactive( address staker, uint256 amount ) internal { // Decrease the active balance for the next epoch. // Always settle total active balance before settling a staker active balance. _decreaseNextBalance(address(0), true, amount); _decreaseNextBalance(staker, true, amount); // Increase the inactive balance for the next epoch. _increaseNextBalance(address(0), false, amount); _increaseNextBalance(staker, false, amount); // Note that we don't need to settle rewards since the current active balance did not change. } function _transferCurrentAndNextActiveBalance( address sender, address recipient, uint256 amount ) internal { // Always settle total active balance before settling a staker active balance. uint256 totalBalance = _settleTotalActiveBalance(); // Move current and next active balances from sender to recipient. uint256 oldSenderBalance = _decreaseCurrentAndNextBalances(sender, true, amount); uint256 oldRecipientBalance = _increaseCurrentAndNextBalances(recipient, true, amount); // When an active balance changes at current timestamp, settle rewards to the current timestamp. _settleUserRewardsUpToNow(sender, oldSenderBalance, totalBalance); _settleUserRewardsUpToNow(recipient, oldRecipientBalance, totalBalance); } function _decreaseCurrentAndNextInactiveBalance( address staker, uint256 amount ) internal { // Decrease the inactive balance for the next epoch. _decreaseCurrentAndNextBalances(address(0), false, amount); _decreaseCurrentAndNextBalances(staker, false, amount); // Note that we don't settle rewards since active balances are not affected. } function _settleTotalActiveBalance() internal returns (uint256) { return _settleBalance(address(0), true); } function _settleAndClaimRewards( address staker, address recipient ) internal returns (uint256) { // Always settle total active balance before settling a staker active balance. uint256 totalBalance = _settleTotalActiveBalance(); // Always settle staker active balance before settling staker rewards. uint256 userBalance = _settleBalance(staker, true); // Settle rewards balance since we want to claim the full accrued amount. _settleUserRewardsUpToNow(staker, userBalance, totalBalance); // Claim rewards balance. return _claimRewards(staker, recipient); } // ============ Private Functions ============ /** * @dev Load a balance for update and then store it. */ function _settleBalance( address maybeStaker, bool isActiveBalance ) private returns (uint256) { SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance); SM1Types.StoredBalance memory balance = _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance); uint256 currentBalance = uint256(balance.currentEpochBalance); _storeBalance(balancePtr, balance); return currentBalance; } /** * @dev Settle a balance while applying an increase. */ function _increaseCurrentAndNextBalances( address maybeStaker, bool isActiveBalance, uint256 amount ) private returns (uint256) { SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance); SM1Types.StoredBalance memory balance = _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance); uint256 originalCurrentBalance = uint256(balance.currentEpochBalance); balance.currentEpochBalance = originalCurrentBalance.add(amount).toUint240(); balance.nextEpochBalance = uint256(balance.nextEpochBalance).add(amount).toUint240(); _storeBalance(balancePtr, balance); return originalCurrentBalance; } /** * @dev Settle a balance while applying a decrease. */ function _decreaseCurrentAndNextBalances( address maybeStaker, bool isActiveBalance, uint256 amount ) private returns (uint256) { SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance); SM1Types.StoredBalance memory balance = _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance); uint256 originalCurrentBalance = uint256(balance.currentEpochBalance); balance.currentEpochBalance = originalCurrentBalance.sub(amount).toUint240(); balance.nextEpochBalance = uint256(balance.nextEpochBalance).sub(amount).toUint240(); _storeBalance(balancePtr, balance); return originalCurrentBalance; } /** * @dev Settle a balance while applying an increase. */ function _increaseNextBalance( address maybeStaker, bool isActiveBalance, uint256 amount ) private { SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance); SM1Types.StoredBalance memory balance = _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance); balance.nextEpochBalance = uint256(balance.nextEpochBalance).add(amount).toUint240(); _storeBalance(balancePtr, balance); } /** * @dev Settle a balance while applying a decrease. */ function _decreaseNextBalance( address maybeStaker, bool isActiveBalance, uint256 amount ) private { SM1Types.StoredBalance storage balancePtr = _getBalancePtr(maybeStaker, isActiveBalance); SM1Types.StoredBalance memory balance = _loadBalanceForUpdate(balancePtr, maybeStaker, isActiveBalance); balance.nextEpochBalance = uint256(balance.nextEpochBalance).sub(amount).toUint240(); _storeBalance(balancePtr, balance); } function _getBalancePtr( address maybeStaker, bool isActiveBalance ) private view returns (SM1Types.StoredBalance storage) { // Active. if (isActiveBalance) { if (maybeStaker != address(0)) { return _ACTIVE_BALANCES_[maybeStaker]; } return _TOTAL_ACTIVE_BALANCE_; } // Inactive. if (maybeStaker != address(0)) { return _INACTIVE_BALANCES_[maybeStaker]; } return _TOTAL_INACTIVE_BALANCE_; } /** * @dev Load a balance for updating. * * IMPORTANT: This function may modify state, and so the balance MUST be stored afterwards. * - For active balances: * - If a rollover occurs, rewards are settled up to the epoch boundary. * * @param balancePtr A storage pointer to the balance. * @param maybeStaker The user address, or address(0) to update total balance. * @param isActiveBalance Whether the balance is an active balance. */ function _loadBalanceForUpdate( SM1Types.StoredBalance storage balancePtr, address maybeStaker, bool isActiveBalance ) private returns (SM1Types.StoredBalance memory) { // Active balance. if (isActiveBalance) { ( SM1Types.StoredBalance memory balance, uint256 beforeRolloverEpoch, uint256 beforeRolloverBalance, bool didRolloverOccur ) = _loadActiveBalance(balancePtr); if (didRolloverOccur) { // Handle the effect of the balance rollover on rewards. We must partially settle the index // up to the epoch boundary where the change in balance occurred. We pass in the balance // from before the boundary. if (maybeStaker == address(0)) { // If it's the total active balance... _settleGlobalIndexUpToEpoch(beforeRolloverBalance, beforeRolloverEpoch); } else { // If it's a user active balance... _settleUserRewardsUpToEpoch(maybeStaker, beforeRolloverBalance, beforeRolloverEpoch); } } return balance; } // Inactive balance. return _loadInactiveBalance(balancePtr); } function _loadActiveBalance( SM1Types.StoredBalance storage balancePtr ) private view returns ( SM1Types.StoredBalance memory, uint256, uint256, bool ) { SM1Types.StoredBalance memory balance = balancePtr; // Return these as they may be needed for rewards settlement. uint256 beforeRolloverEpoch = uint256(balance.currentEpoch); uint256 beforeRolloverBalance = uint256(balance.currentEpochBalance); bool didRolloverOccur = false; // Roll the balance forward if needed. uint256 currentEpoch = getCurrentEpoch(); if (currentEpoch > uint256(balance.currentEpoch)) { didRolloverOccur = balance.currentEpochBalance != balance.nextEpochBalance; balance.currentEpoch = currentEpoch.toUint16(); balance.currentEpochBalance = balance.nextEpochBalance; } return (balance, beforeRolloverEpoch, beforeRolloverBalance, didRolloverOccur); } function _loadInactiveBalance( SM1Types.StoredBalance storage balancePtr ) private view returns (SM1Types.StoredBalance memory) { SM1Types.StoredBalance memory balance = balancePtr; // Roll the balance forward if needed. uint256 currentEpoch = getCurrentEpoch(); if (currentEpoch > uint256(balance.currentEpoch)) { balance.currentEpoch = currentEpoch.toUint16(); balance.currentEpochBalance = balance.nextEpochBalance; } return balance; } /** * @dev Store a balance. */ function _storeBalance( SM1Types.StoredBalance storage balancePtr, SM1Types.StoredBalance memory balance ) private { // Note: This should use a single `sstore` when compiler optimizations are enabled. balancePtr.currentEpoch = balance.currentEpoch; balancePtr.currentEpochBalance = balance.currentEpochBalance; balancePtr.nextEpochBalance = balance.nextEpochBalance; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { AccessControlUpgradeable } from '../../../dependencies/open-zeppelin/AccessControlUpgradeable.sol'; import { ReentrancyGuard } from '../../../utils/ReentrancyGuard.sol'; import { VersionedInitializable } from '../../../utils/VersionedInitializable.sol'; import { SM1Types } from '../lib/SM1Types.sol'; /** * @title SM1Storage * @author dYdX * * @dev Storage contract. Contains or inherits from all contract with storage. */ abstract contract SM1Storage is AccessControlUpgradeable, ReentrancyGuard, VersionedInitializable { // ============ Epoch Schedule ============ /// @dev The parameters specifying the function from timestamp to epoch number. SM1Types.EpochParameters internal _EPOCH_PARAMETERS_; /// @dev The period of time at the end of each epoch in which withdrawals cannot be requested. uint256 internal _BLACKOUT_WINDOW_; // ============ Staked Token ERC20 ============ /// @dev Allowances for ERC-20 transfers. mapping(address => mapping(address => uint256)) internal _ALLOWANCES_; // ============ Governance Power Delegation ============ /// @dev Domain separator for EIP-712 signatures. bytes32 internal _DOMAIN_SEPARATOR_; /// @dev Mapping from (owner) => (next valid nonce) for EIP-712 signatures. mapping(address => uint256) internal _NONCES_; /// @dev Snapshots and delegates for governance voting power. mapping(address => mapping(uint256 => SM1Types.Snapshot)) internal _VOTING_SNAPSHOTS_; mapping(address => uint256) internal _VOTING_SNAPSHOT_COUNTS_; mapping(address => address) internal _VOTING_DELEGATES_; /// @dev Snapshots and delegates for governance proposition power. mapping(address => mapping(uint256 => SM1Types.Snapshot)) internal _PROPOSITION_SNAPSHOTS_; mapping(address => uint256) internal _PROPOSITION_SNAPSHOT_COUNTS_; mapping(address => address) internal _PROPOSITION_DELEGATES_; // ============ Rewards Accounting ============ /// @dev The emission rate of rewards. uint256 internal _REWARDS_PER_SECOND_; /// @dev The cumulative rewards earned per staked token. (Shared storage slot.) uint224 internal _GLOBAL_INDEX_; /// @dev The timestamp at which the global index was last updated. (Shared storage slot.) uint32 internal _GLOBAL_INDEX_TIMESTAMP_; /// @dev The value of the global index when the user's staked balance was last updated. mapping(address => uint256) internal _USER_INDEXES_; /// @dev The user's accrued, unclaimed rewards (as of the last update to the user index). mapping(address => uint256) internal _USER_REWARDS_BALANCES_; /// @dev The value of the global index at the end of a given epoch. mapping(uint256 => uint256) internal _EPOCH_INDEXES_; // ============ Staker Accounting ============ /// @dev The active balance by staker. mapping(address => SM1Types.StoredBalance) internal _ACTIVE_BALANCES_; /// @dev The total active balance of stakers. SM1Types.StoredBalance internal _TOTAL_ACTIVE_BALANCE_; /// @dev The inactive balance by staker. mapping(address => SM1Types.StoredBalance) internal _INACTIVE_BALANCES_; /// @dev The total inactive balance of stakers. SM1Types.StoredBalance internal _TOTAL_INACTIVE_BALANCE_; // ============ Exchange Rate ============ /// @dev The value of one underlying token, in the units used for staked balances, denominated /// as a mutiple of EXCHANGE_RATE_BASE for additional precision. uint256 internal _EXCHANGE_RATE_; /// @dev Historical snapshots of the exchange rate, in each block that it has changed. mapping(uint256 => SM1Types.Snapshot) internal _EXCHANGE_RATE_SNAPSHOTS_; /// @dev Number of snapshots of the exchange rate. uint256 internal _EXCHANGE_RATE_SNAPSHOT_COUNT_; } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './Context.sol'; import './Strings.sol'; import './ERC165.sol'; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { function hasRole(bytes32 role, address account) external view returns (bool); function getRoleAdmin(bytes32 role) external view returns (bytes32); function grantRole(bytes32 role, address account) external; function revokeRole(bytes32 role, address account) external; function renounceRole(bytes32 role, address account) external; } /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControlUpgradeable is Context, IAccessControlUpgradeable, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged( bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole ); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/ */ function _checkRole(bytes32 role, address account) internal view { if (!hasRole(role, account)) { revert( string( abi.encodePacked( 'AccessControl: account ', Strings.toHexString(uint160(account), 20), ' is missing role ', Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), 'AccessControl: can only renounce roles for self'); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); _roles[role].adminRole = adminRole; } function _grantRole(bytes32 role, address account) private { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } function _revokeRole(bytes32 role, address account) private { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } uint256[49] private __gap; } // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.5; pragma abicoder v2; /** * @title ReentrancyGuard * @author dYdX * * @dev Updated ReentrancyGuard library designed to be used with Proxy Contracts. */ abstract contract ReentrancyGuard { uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = uint256(int256(-1)); uint256 private _STATUS_; constructor() internal { _STATUS_ = NOT_ENTERED; } modifier nonReentrant() { require(_STATUS_ != ENTERED, 'ReentrancyGuard: reentrant call'); _STATUS_ = ENTERED; _; _STATUS_ = NOT_ENTERED; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; /** * @title VersionedInitializable * @author Aave, inspired by the OpenZeppelin Initializable contract * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. * */ abstract contract VersionedInitializable { /** * @dev Indicates that the contract has been initialized. */ uint256 internal lastInitializedRevision = 0; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { uint256 revision = getRevision(); require(revision > lastInitializedRevision, "Contract instance has already been initialized"); lastInitializedRevision = revision; _; } /// @dev returns the revision number of the contract. /// Needs to be defined in the inherited class as a constant. function getRevision() internal pure virtual returns(uint256); // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev String operations. */ library Strings { bytes16 private constant alphabet = '0123456789abcdef'; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return '0'; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return '0x00'; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = '0'; buffer[1] = 'x'; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = alphabet[value & 0xf]; value >>= 4; } require(value == 0, 'Strings: hex length insufficient'); return string(buffer); } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import './IERC165.sol'; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } } // SPDX-License-Identifier: MIT pragma solidity 0.7.5; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; /** * @dev Methods for downcasting unsigned integers, reverting on overflow. */ library SafeCast { /** * @dev Downcast to a uint16, reverting on overflow. */ function toUint16( uint256 a ) internal pure returns (uint16) { uint16 b = uint16(a); require( uint256(b) == a, 'SafeCast: toUint16 overflow' ); return b; } /** * @dev Downcast to a uint32, reverting on overflow. */ function toUint32( uint256 a ) internal pure returns (uint32) { uint32 b = uint32(a); require( uint256(b) == a, 'SafeCast: toUint32 overflow' ); return b; } /** * @dev Downcast to a uint128, reverting on overflow. */ function toUint128( uint256 a ) internal pure returns (uint128) { uint128 b = uint128(a); require( uint256(b) == a, 'SafeCast: toUint128 overflow' ); return b; } /** * @dev Downcast to a uint224, reverting on overflow. */ function toUint224( uint256 a ) internal pure returns (uint224) { uint224 b = uint224(a); require( uint256(b) == a, 'SafeCast: toUint224 overflow' ); return b; } /** * @dev Downcast to a uint240, reverting on overflow. */ function toUint240( uint256 a ) internal pure returns (uint240) { uint240 b = uint240(a); require( uint256(b) == a, 'SafeCast: toUint240 overflow' ); return b; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeERC20 } from '../../../dependencies/open-zeppelin/SafeERC20.sol'; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { Math } from '../../../utils/Math.sol'; import { SafeCast } from '../lib/SafeCast.sol'; import { SM1EpochSchedule } from './SM1EpochSchedule.sol'; /** * @title SM1Rewards * @author dYdX * * @dev Manages the distribution of token rewards. * * Rewards are distributed continuously. After each second, an account earns rewards `r` according * to the following formula: * * r = R * s / S * * Where: * - `R` is the rewards distributed globally each second, also called the “emission rate.” * - `s` is the account's staked balance in that second (technically, it is measured at the * end of the second) * - `S` is the sum total of all staked balances in that second (again, measured at the end of * the second) * * The parameter `R` can be configured by the contract owner. For every second that elapses, * exactly `R` tokens will accrue to users, save for rounding errors, and with the exception that * while the total staked balance is zero, no tokens will accrue to anyone. * * The accounting works as follows: A global index is stored which represents the cumulative * number of rewards tokens earned per staked token since the start of the distribution. * The value of this index increases over time, and there are two factors affecting the rate of * increase: * 1) The emission rate (in the numerator) * 2) The total number of staked tokens (in the denominator) * * Whenever either factor changes, in some timestamp T, we settle the global index up to T by * calculating the increase in the index since the last update using the OLD values of the factors: * * indexDelta = timeDelta * emissionPerSecond * INDEX_BASE / totalStaked * * Where `INDEX_BASE` is a scaling factor used to allow more precision in the storage of the index. * * For each user we store an accrued rewards balance, as well as a user index, which is a cache of * the global index at the time that the user's accrued rewards balance was last updated. Then at * any point in time, a user's claimable rewards are represented by the following: * * rewards = _USER_REWARDS_BALANCES_[user] + userStaked * ( * settledGlobalIndex - _USER_INDEXES_[user] * ) / INDEX_BASE */ abstract contract SM1Rewards is SM1EpochSchedule { using SafeCast for uint256; using SafeERC20 for IERC20; using SafeMath for uint256; // ============ Constants ============ /// @dev Additional precision used to represent the global and user index values. uint256 private constant INDEX_BASE = 10**18; /// @notice The rewards token. IERC20 public immutable REWARDS_TOKEN; /// @notice Address to pull rewards from. Must have provided an allowance to this contract. address public immutable REWARDS_TREASURY; /// @notice Start timestamp (inclusive) of the period in which rewards can be earned. uint256 public immutable DISTRIBUTION_START; /// @notice End timestamp (exclusive) of the period in which rewards can be earned. uint256 public immutable DISTRIBUTION_END; // ============ Events ============ event RewardsPerSecondUpdated( uint256 emissionPerSecond ); event GlobalIndexUpdated( uint256 index ); event UserIndexUpdated( address indexed user, uint256 index, uint256 unclaimedRewards ); event ClaimedRewards( address indexed user, address recipient, uint256 claimedRewards ); // ============ Constructor ============ constructor( IERC20 rewardsToken, address rewardsTreasury, uint256 distributionStart, uint256 distributionEnd ) { require( distributionEnd >= distributionStart, 'SM1Rewards: Invalid parameters' ); REWARDS_TOKEN = rewardsToken; REWARDS_TREASURY = rewardsTreasury; DISTRIBUTION_START = distributionStart; DISTRIBUTION_END = distributionEnd; } // ============ External Functions ============ /** * @notice The current emission rate of rewards. * * @return The number of rewards tokens issued globally each second. */ function getRewardsPerSecond() external view returns (uint256) { return _REWARDS_PER_SECOND_; } // ============ Internal Functions ============ /** * @dev Initialize the contract. */ function __SM1Rewards_init() internal { _GLOBAL_INDEX_TIMESTAMP_ = Math.max(block.timestamp, DISTRIBUTION_START).toUint32(); } /** * @dev Set the emission rate of rewards. * * IMPORTANT: Do not call this function without settling the total staked balance first, to * ensure that the index is settled up to the epoch boundaries. * * @param emissionPerSecond The new number of rewards tokens to give out each second. * @param totalStaked The total staked balance. */ function _setRewardsPerSecond( uint256 emissionPerSecond, uint256 totalStaked ) internal { _settleGlobalIndexUpToNow(totalStaked); _REWARDS_PER_SECOND_ = emissionPerSecond; emit RewardsPerSecondUpdated(emissionPerSecond); } /** * @dev Claim tokens, sending them to the specified recipient. * * Note: In order to claim all accrued rewards, the total and user staked balances must first be * settled before calling this function. * * @param user The user's address. * @param recipient The address to send rewards to. * * @return The number of rewards tokens claimed. */ function _claimRewards( address user, address recipient ) internal returns (uint256) { uint256 accruedRewards = _USER_REWARDS_BALANCES_[user]; _USER_REWARDS_BALANCES_[user] = 0; REWARDS_TOKEN.safeTransferFrom(REWARDS_TREASURY, recipient, accruedRewards); emit ClaimedRewards(user, recipient, accruedRewards); return accruedRewards; } /** * @dev Settle a user's rewards up to the latest global index as of `block.timestamp`. Triggers a * settlement of the global index up to `block.timestamp`. Should be called with the OLD user * and total balances. * * @param user The user's address. * @param userStaked Tokens staked by the user during the period since the last user index * update. * @param totalStaked Total tokens staked by all users during the period since the last global * index update. * * @return The user's accrued rewards, including past unclaimed rewards. */ function _settleUserRewardsUpToNow( address user, uint256 userStaked, uint256 totalStaked ) internal returns (uint256) { uint256 globalIndex = _settleGlobalIndexUpToNow(totalStaked); return _settleUserRewardsUpToIndex(user, userStaked, globalIndex); } /** * @dev Settle a user's rewards up to an epoch boundary. Should be used to partially settle a * user's rewards if their balance was known to have changed on that epoch boundary. * * @param user The user's address. * @param userStaked Tokens staked by the user. Should be accurate for the time period * since the last update to this user and up to the end of the * specified epoch. * @param epochNumber Settle the user's rewards up to the end of this epoch. * * @return The user's accrued rewards, including past unclaimed rewards, up to the end of the * specified epoch. */ function _settleUserRewardsUpToEpoch( address user, uint256 userStaked, uint256 epochNumber ) internal returns (uint256) { uint256 globalIndex = _EPOCH_INDEXES_[epochNumber]; return _settleUserRewardsUpToIndex(user, userStaked, globalIndex); } /** * @dev Settle the global index up to the end of the given epoch. * * IMPORTANT: This function should only be called under conditions which ensure the following: * - `epochNumber` < the current epoch number * - `_GLOBAL_INDEX_TIMESTAMP_ < settleUpToTimestamp` * - `_EPOCH_INDEXES_[epochNumber] = 0` */ function _settleGlobalIndexUpToEpoch( uint256 totalStaked, uint256 epochNumber ) internal returns (uint256) { uint256 settleUpToTimestamp = getStartOfEpoch(epochNumber.add(1)); uint256 globalIndex = _settleGlobalIndexUpToTimestamp(totalStaked, settleUpToTimestamp); _EPOCH_INDEXES_[epochNumber] = globalIndex; return globalIndex; } // ============ Private Functions ============ /** * @dev Updates the global index, reflecting cumulative rewards given out per staked token. * * @param totalStaked The total staked balance, which should be constant in the interval * since the last update to the global index. * * @return The new global index. */ function _settleGlobalIndexUpToNow( uint256 totalStaked ) private returns (uint256) { return _settleGlobalIndexUpToTimestamp(totalStaked, block.timestamp); } /** * @dev Helper function which settles a user's rewards up to a global index. Should be called * any time a user's staked balance changes, with the OLD user and total balances. * * @param user The user's address. * @param userStaked Tokens staked by the user during the period since the last user index * update. * @param newGlobalIndex The new index value to bring the user index up to. MUST NOT be less * than the user's index. * * @return The user's accrued rewards, including past unclaimed rewards. */ function _settleUserRewardsUpToIndex( address user, uint256 userStaked, uint256 newGlobalIndex ) private returns (uint256) { uint256 oldAccruedRewards = _USER_REWARDS_BALANCES_[user]; uint256 oldUserIndex = _USER_INDEXES_[user]; if (oldUserIndex == newGlobalIndex) { return oldAccruedRewards; } uint256 newAccruedRewards; if (userStaked == 0) { // Note: Even if the user's staked balance is zero, we still need to update the user index. newAccruedRewards = oldAccruedRewards; } else { // Calculate newly accrued rewards since the last update to the user's index. uint256 indexDelta = newGlobalIndex.sub(oldUserIndex); uint256 accruedRewardsDelta = userStaked.mul(indexDelta).div(INDEX_BASE); newAccruedRewards = oldAccruedRewards.add(accruedRewardsDelta); // Update the user's rewards. _USER_REWARDS_BALANCES_[user] = newAccruedRewards; } // Update the user's index. _USER_INDEXES_[user] = newGlobalIndex; emit UserIndexUpdated(user, newGlobalIndex, newAccruedRewards); return newAccruedRewards; } /** * @dev Updates the global index, reflecting cumulative rewards given out per staked token. * * @param totalStaked The total staked balance, which should be constant in the interval * (_GLOBAL_INDEX_TIMESTAMP_, settleUpToTimestamp). * @param settleUpToTimestamp The timestamp up to which to settle rewards. It MUST satisfy * `settleUpToTimestamp <= block.timestamp`. * * @return The new global index. */ function _settleGlobalIndexUpToTimestamp( uint256 totalStaked, uint256 settleUpToTimestamp ) private returns (uint256) { uint256 oldGlobalIndex = uint256(_GLOBAL_INDEX_); // The goal of this function is to calculate rewards earned since the last global index update. // These rewards are earned over the time interval which is the intersection of the intervals // [_GLOBAL_INDEX_TIMESTAMP_, settleUpToTimestamp] and [DISTRIBUTION_START, DISTRIBUTION_END]. // // We can simplify a bit based on the assumption: // `_GLOBAL_INDEX_TIMESTAMP_ >= DISTRIBUTION_START` // // Get the start and end of the time interval under consideration. uint256 intervalStart = uint256(_GLOBAL_INDEX_TIMESTAMP_); uint256 intervalEnd = Math.min(settleUpToTimestamp, DISTRIBUTION_END); // Return early if the interval has length zero (incl. case where intervalEnd < intervalStart). if (intervalEnd <= intervalStart) { return oldGlobalIndex; } // Note: If we reach this point, we must update _GLOBAL_INDEX_TIMESTAMP_. uint256 emissionPerSecond = _REWARDS_PER_SECOND_; if (emissionPerSecond == 0 || totalStaked == 0) { // Ensure a log is emitted if the timestamp changed, even if the index does not change. _GLOBAL_INDEX_TIMESTAMP_ = intervalEnd.toUint32(); emit GlobalIndexUpdated(oldGlobalIndex); return oldGlobalIndex; } // Calculate the change in index over the interval. uint256 timeDelta = intervalEnd.sub(intervalStart); uint256 indexDelta = timeDelta.mul(emissionPerSecond).mul(INDEX_BASE).div(totalStaked); // Calculate, update, and return the new global index. uint256 newGlobalIndex = oldGlobalIndex.add(indexDelta); // Update storage. (Shared storage slot.) _GLOBAL_INDEX_TIMESTAMP_ = intervalEnd.toUint32(); _GLOBAL_INDEX_ = newGlobalIndex.toUint224(); emit GlobalIndexUpdated(newGlobalIndex); return newGlobalIndex; } } // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../dependencies/open-zeppelin/SafeMath.sol'; /** * @title Math * @author dYdX * * @dev Library for non-standard Math functions. */ library Math { using SafeMath for uint256; // ============ Library Functions ============ /** * @dev Return `ceil(numerator / denominator)`. */ function divRoundUp( uint256 numerator, uint256 denominator ) internal pure returns (uint256) { if (numerator == 0) { // SafeMath will check for zero denominator return SafeMath.div(0, denominator); } return numerator.sub(1).div(denominator).add(1); } /** * @dev Returns the minimum between a and b. */ function min( uint256 a, uint256 b ) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the maximum between a and b. */ function max( uint256 a, uint256 b ) internal pure returns (uint256) { return a > b ? a : b; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { SafeCast } from '../lib/SafeCast.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1EpochSchedule * @author dYdX * * @dev Defines a function from block timestamp to epoch number. * * The formula used is `n = floor((t - b) / a)` where: * - `n` is the epoch number * - `t` is the timestamp (in seconds) * - `b` is a non-negative offset, indicating the start of epoch zero (in seconds) * - `a` is the length of an epoch, a.k.a. the interval (in seconds) * * Note that by restricting `b` to be non-negative, we limit ourselves to functions in which epoch * zero starts at a non-negative timestamp. * * The recommended epoch length and blackout window are 28 and 7 days respectively; however, these * are modifiable by the admin, within the specified bounds. */ abstract contract SM1EpochSchedule is SM1Storage { using SafeCast for uint256; using SafeMath for uint256; // ============ Events ============ event EpochParametersChanged( SM1Types.EpochParameters epochParameters ); event BlackoutWindowChanged( uint256 blackoutWindow ); // ============ Initializer ============ function __SM1EpochSchedule_init( uint256 interval, uint256 offset, uint256 blackoutWindow ) internal { require( block.timestamp < offset, 'SM1EpochSchedule: Epoch zero must start after initialization' ); _setBlackoutWindow(blackoutWindow); _setEpochParameters(interval, offset); } // ============ Public Functions ============ /** * @notice Get the epoch at the current block timestamp. * * NOTE: Reverts if epoch zero has not started. * * @return The current epoch number. */ function getCurrentEpoch() public view returns (uint256) { (uint256 interval, uint256 offsetTimestamp) = _getIntervalAndOffsetTimestamp(); return offsetTimestamp.div(interval); } /** * @notice Get the time remaining in the current epoch. * * NOTE: Reverts if epoch zero has not started. * * @return The number of seconds until the next epoch. */ function getTimeRemainingInCurrentEpoch() public view returns (uint256) { (uint256 interval, uint256 offsetTimestamp) = _getIntervalAndOffsetTimestamp(); uint256 timeElapsedInEpoch = offsetTimestamp.mod(interval); return interval.sub(timeElapsedInEpoch); } /** * @notice Given an epoch number, get the start of that epoch. Calculated as `t = (n * a) + b`. * * @return The timestamp in seconds representing the start of that epoch. */ function getStartOfEpoch( uint256 epochNumber ) public view returns (uint256) { SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_; uint256 interval = uint256(epochParameters.interval); uint256 offset = uint256(epochParameters.offset); return epochNumber.mul(interval).add(offset); } /** * @notice Check whether we are at or past the start of epoch zero. * * @return Boolean `true` if the current timestamp is at least the start of epoch zero, * otherwise `false`. */ function hasEpochZeroStarted() public view returns (bool) { SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_; uint256 offset = uint256(epochParameters.offset); return block.timestamp >= offset; } /** * @notice Check whether we are in a blackout window, where withdrawal requests are restricted. * Note that before epoch zero has started, there are no blackout windows. * * @return Boolean `true` if we are in a blackout window, otherwise `false`. */ function inBlackoutWindow() public view returns (bool) { return hasEpochZeroStarted() && getTimeRemainingInCurrentEpoch() <= _BLACKOUT_WINDOW_; } // ============ Internal Functions ============ function _setEpochParameters( uint256 interval, uint256 offset ) internal { SM1Types.EpochParameters memory epochParameters = SM1Types.EpochParameters({interval: interval.toUint128(), offset: offset.toUint128()}); _EPOCH_PARAMETERS_ = epochParameters; emit EpochParametersChanged(epochParameters); } function _setBlackoutWindow( uint256 blackoutWindow ) internal { _BLACKOUT_WINDOW_ = blackoutWindow; emit BlackoutWindowChanged(blackoutWindow); } // ============ Private Functions ============ /** * @dev Helper function to read params from storage and apply offset to the given timestamp. * Recall that the formula for epoch number is `n = (t - b) / a`. * * NOTE: Reverts if epoch zero has not started. * * @return The values `a` and `(t - b)`. */ function _getIntervalAndOffsetTimestamp() private view returns (uint256, uint256) { SM1Types.EpochParameters memory epochParameters = _EPOCH_PARAMETERS_; uint256 interval = uint256(epochParameters.interval); uint256 offset = uint256(epochParameters.offset); require( block.timestamp >= offset, 'SM1EpochSchedule: Epoch zero has not started' ); uint256 offsetTimestamp = block.timestamp.sub(offset); return (interval, offsetTimestamp); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IERC20 } from '../../../interfaces/IERC20.sol'; import { IERC20Detailed } from '../../../interfaces/IERC20Detailed.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1GovernancePowerDelegation } from './SM1GovernancePowerDelegation.sol'; import { SM1StakedBalances } from './SM1StakedBalances.sol'; /** * @title SM1ERC20 * @author dYdX * * @dev ERC20 interface for staked tokens. Implements governance functionality for the tokens. * * Also allows a user with an active stake to transfer their staked tokens to another user, * even if they would otherwise be restricted from withdrawing. */ abstract contract SM1ERC20 is SM1StakedBalances, SM1GovernancePowerDelegation, IERC20Detailed { using SafeMath for uint256; // ============ Constants ============ /// @notice EIP-712 typehash for token approval via EIP-2612 permit. bytes32 public constant PERMIT_TYPEHASH = keccak256( 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)' ); // ============ External Functions ============ function name() external pure override returns (string memory) { return 'Staked DYDX'; } function symbol() external pure override returns (string memory) { return 'stkDYDX'; } function decimals() external pure override returns (uint8) { return 18; } /** * @notice Get the total supply of staked balances. * * Note that due to the exchange rate, this is different than querying the total balance of * underyling token staked to this contract. * * @return The sum of all staked balances. */ function totalSupply() external view override returns (uint256) { return getTotalActiveBalanceCurrentEpoch() + getTotalInactiveBalanceCurrentEpoch(); } /** * @notice Get a user's staked balance. * * Note that due to the exchange rate, one unit of staked balance may not be equivalent to one * unit of the underlying token. Also note that a user's staked balance is different from a * user's transferable balance. * * @param account The account to get the balance of. * * @return The user's staked balance. */ function balanceOf( address account ) public view override(SM1GovernancePowerDelegation, IERC20) returns (uint256) { return getActiveBalanceCurrentEpoch(account) + getInactiveBalanceCurrentEpoch(account); } function transfer( address recipient, uint256 amount ) external override nonReentrant returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance( address owner, address spender ) external view override returns (uint256) { return _ALLOWANCES_[owner][spender]; } function approve( address spender, uint256 amount ) external override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom( address sender, address recipient, uint256 amount ) external override nonReentrant returns (bool) { _transfer(sender, recipient, amount); _approve( sender, msg.sender, _ALLOWANCES_[sender][msg.sender].sub(amount, 'SM1ERC20: transfer amount exceeds allowance') ); return true; } function increaseAllowance( address spender, uint256 addedValue ) external returns (bool) { _approve(msg.sender, spender, _ALLOWANCES_[msg.sender][spender].add(addedValue)); return true; } function decreaseAllowance( address spender, uint256 subtractedValue ) external returns (bool) { _approve( msg.sender, spender, _ALLOWANCES_[msg.sender][spender].sub( subtractedValue, 'SM1ERC20: Decreased allowance below zero' ) ); return true; } /** * @notice Implements the permit function as specified in EIP-2612. * * @param owner Address of the token owner. * @param spender Address of the spender. * @param value Amount of allowance. * @param deadline Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require( owner != address(0), 'SM1ERC20: INVALID_OWNER' ); require( block.timestamp <= deadline, 'SM1ERC20: INVALID_EXPIRATION' ); uint256 currentValidNonce = _NONCES_[owner]; bytes32 digest = keccak256( abi.encodePacked( '\\x19\\x01', _DOMAIN_SEPARATOR_, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) ) ); require( owner == ecrecover(digest, v, r, s), 'SM1ERC20: INVALID_SIGNATURE' ); _NONCES_[owner] = currentValidNonce.add(1); _approve(owner, spender, value); } // ============ Internal Functions ============ function _transfer( address sender, address recipient, uint256 amount ) internal { require( sender != address(0), 'SM1ERC20: Transfer from address(0)' ); require( recipient != address(0), 'SM1ERC20: Transfer to address(0)' ); require( getTransferableBalance(sender) >= amount, 'SM1ERC20: Transfer exceeds next epoch active balance' ); // Update staked balances and delegate snapshots. _transferCurrentAndNextActiveBalance(sender, recipient, amount); _moveDelegatesForTransfer(sender, recipient, amount); emit Transfer(sender, recipient, amount); } function _approve( address owner, address spender, uint256 amount ) internal { require( owner != address(0), 'SM1ERC20: Approve from address(0)' ); require( spender != address(0), 'SM1ERC20: Approve to address(0)' ); _ALLOWANCES_[owner][spender] = amount; emit Approval(owner, spender, amount); } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; import { IERC20 } from './IERC20.sol'; /** * @dev Interface for ERC20 including metadata **/ interface IERC20Detailed is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { IGovernancePowerDelegationERC20 } from '../../../interfaces/IGovernancePowerDelegationERC20.sol'; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1ExchangeRate } from './SM1ExchangeRate.sol'; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1GovernancePowerDelegation * @author dYdX * * @dev Provides support for two types of governance powers which are separately delegatable. * Provides functions for delegation and for querying a user's power at a certain block number. * * Internally, makes use of staked balances denoted in staked units, but returns underlying token * units from the getPowerAtBlock() and getPowerCurrent() functions. * * This is based on, and is designed to match, Aave's implementation, which is used in their * governance token and staked token contracts. */ abstract contract SM1GovernancePowerDelegation is SM1ExchangeRate, IGovernancePowerDelegationERC20 { using SafeMath for uint256; // ============ Constants ============ /// @notice EIP-712 typehash for delegation by signature of a specific governance power type. bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256( 'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)' ); /// @notice EIP-712 typehash for delegation by signature of all governance powers. bytes32 public constant DELEGATE_TYPEHASH = keccak256( 'Delegate(address delegatee,uint256 nonce,uint256 expiry)' ); // ============ External Functions ============ /** * @notice Delegates a specific governance power of the sender to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType( address delegatee, DelegationType delegationType ) external override { _delegateByType(msg.sender, delegatee, delegationType); } /** * @notice Delegates all governance powers of the sender to a delegatee. * * @param delegatee The address to delegate power to. */ function delegate( address delegatee ) external override { _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER); _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER); } /** * @dev Delegates specific governance power from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateByTypeBySig( address delegatee, DelegationType delegationType, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external { bytes32 structHash = keccak256( abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry) ); bytes32 digest = keccak256(abi.encodePacked('\\x19\\x01', _DOMAIN_SEPARATOR_, structHash)); address signer = ecrecover(digest, v, r, s); require( signer != address(0), 'SM1GovernancePowerDelegation: INVALID_SIGNATURE' ); require( nonce == _NONCES_[signer]++, 'SM1GovernancePowerDelegation: INVALID_NONCE' ); require( block.timestamp <= expiry, 'SM1GovernancePowerDelegation: INVALID_EXPIRATION' ); _delegateByType(signer, delegatee, delegationType); } /** * @dev Delegates both governance powers from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateBySig( address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external { bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked('\\x19\\x01', _DOMAIN_SEPARATOR_, structHash)); address signer = ecrecover(digest, v, r, s); require( signer != address(0), 'SM1GovernancePowerDelegation: INVALID_SIGNATURE' ); require( nonce == _NONCES_[signer]++, 'SM1GovernancePowerDelegation: INVALID_NONCE' ); require( block.timestamp <= expiry, 'SM1GovernancePowerDelegation: INVALID_EXPIRATION' ); _delegateByType(signer, delegatee, DelegationType.VOTING_POWER); _delegateByType(signer, delegatee, DelegationType.PROPOSITION_POWER); } /** * @notice Returns the delegatee of a user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType( address delegator, DelegationType delegationType ) external override view returns (address) { (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); return _getDelegatee(delegator, delegates); } /** * @notice Returns the current power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent( address user, DelegationType delegationType ) external override view returns (uint256) { return getPowerAtBlock(user, block.number, delegationType); } /** * @notice Get the next valid nonce for EIP-712 signatures. * * This nonce should be used when signing for any of the following functions: * - permit() * - delegateByTypeBySig() * - delegateBySig() */ function nonces( address owner ) external view returns (uint256) { return _NONCES_[owner]; } // ============ Public Functions ============ function balanceOf( address account ) public view virtual returns (uint256); /** * @notice Returns the power of a user at a certain block, denominated in underlying token units. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). * * @return The user's governance power of the specified type, in underlying token units. */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) public override view returns (uint256) { ( mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotCounts, // unused: delegates ) = _getDelegationDataByType(delegationType); uint256 stakeAmount = _findValueAtBlock( snapshots[user], snapshotCounts[user], blockNumber, 0 ); uint256 exchangeRate = _findValueAtBlock( _EXCHANGE_RATE_SNAPSHOTS_, _EXCHANGE_RATE_SNAPSHOT_COUNT_, blockNumber, EXCHANGE_RATE_BASE ); return underlyingAmountFromStakeAmountWithExchangeRate(stakeAmount, exchangeRate); } // ============ Internal Functions ============ /** * @dev Delegates one specific power to a delegatee. * * @param delegator The user whose power to delegate. * @param delegatee The address to delegate power to. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _delegateByType( address delegator, address delegatee, DelegationType delegationType ) internal { require( delegatee != address(0), 'SM1GovernancePowerDelegation: INVALID_DELEGATEE' ); (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); uint256 delegatorBalance = balanceOf(delegator); address previousDelegatee = _getDelegatee(delegator, delegates); delegates[delegator] = delegatee; _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType); emit DelegateChanged(delegator, delegatee, delegationType); } /** * @dev Update delegate snapshots whenever staked tokens are transfered, minted, or burned. * * @param from The sender. * @param to The recipient. * @param stakedAmount The amount being transfered, denominated in staked units. */ function _moveDelegatesForTransfer( address from, address to, uint256 stakedAmount ) internal { address votingPowerFromDelegatee = _getDelegatee(from, _VOTING_DELEGATES_); address votingPowerToDelegatee = _getDelegatee(to, _VOTING_DELEGATES_); _moveDelegatesByType( votingPowerFromDelegatee, votingPowerToDelegatee, stakedAmount, DelegationType.VOTING_POWER ); address propositionPowerFromDelegatee = _getDelegatee(from, _PROPOSITION_DELEGATES_); address propositionPowerToDelegatee = _getDelegatee(to, _PROPOSITION_DELEGATES_); _moveDelegatesByType( propositionPowerFromDelegatee, propositionPowerToDelegatee, stakedAmount, DelegationType.PROPOSITION_POWER ); } /** * @dev Moves power from one user to another. * * @param from The user from which delegated power is moved. * @param to The user that will receive the delegated power. * @param amount The amount of power to be moved. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _moveDelegatesByType( address from, address to, uint256 amount, DelegationType delegationType ) internal { if (from == to) { return; } ( mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotCounts, // unused: delegates ) = _getDelegationDataByType(delegationType); if (from != address(0)) { mapping(uint256 => SM1Types.Snapshot) storage fromSnapshots = snapshots[from]; uint256 fromSnapshotCount = snapshotCounts[from]; uint256 previousBalance = 0; if (fromSnapshotCount != 0) { previousBalance = fromSnapshots[fromSnapshotCount - 1].value; } uint256 newBalance = previousBalance.sub(amount); snapshotCounts[from] = _writeSnapshot( fromSnapshots, fromSnapshotCount, newBalance ); emit DelegatedPowerChanged(from, newBalance, delegationType); } if (to != address(0)) { mapping(uint256 => SM1Types.Snapshot) storage toSnapshots = snapshots[to]; uint256 toSnapshotCount = snapshotCounts[to]; uint256 previousBalance = 0; if (toSnapshotCount != 0) { previousBalance = toSnapshots[toSnapshotCount - 1].value; } uint256 newBalance = previousBalance.add(amount); snapshotCounts[to] = _writeSnapshot( toSnapshots, toSnapshotCount, newBalance ); emit DelegatedPowerChanged(to, newBalance, delegationType); } } /** * @dev Returns delegation data (snapshot, snapshotCount, delegates) by delegation type. * * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). * * @return The mapping of each user to a mapping of snapshots. * @return The mapping of each user to the total number of snapshots for that user. * @return The mapping of each user to the user's delegate. */ function _getDelegationDataByType( DelegationType delegationType ) internal view returns ( mapping(address => mapping(uint256 => SM1Types.Snapshot)) storage, mapping(address => uint256) storage, mapping(address => address) storage ) { if (delegationType == DelegationType.VOTING_POWER) { return ( _VOTING_SNAPSHOTS_, _VOTING_SNAPSHOT_COUNTS_, _VOTING_DELEGATES_ ); } else { return ( _PROPOSITION_SNAPSHOTS_, _PROPOSITION_SNAPSHOT_COUNTS_, _PROPOSITION_DELEGATES_ ); } } /** * @dev Returns the delegatee of a user. If a user never performed any delegation, their * delegated address will be 0x0, in which case we return the user's own address. * * @param delegator The address of the user for which return the delegatee. * @param delegates The mapping of delegates for a particular type of delegation. */ function _getDelegatee( address delegator, mapping(address => address) storage delegates ) internal view returns (address) { address previousDelegatee = delegates[delegator]; if (previousDelegatee == address(0)) { return delegator; } return previousDelegatee; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; interface IGovernancePowerDelegationERC20 { enum DelegationType { VOTING_POWER, PROPOSITION_POWER } /** * @dev Emitted when a user delegates governance power to another user. * * @param delegator The delegator. * @param delegatee The delegatee. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegateChanged( address indexed delegator, address indexed delegatee, DelegationType delegationType ); /** * @dev Emitted when an action changes the delegated power of a user. * * @param user The user whose delegated power has changed. * @param amount The new amount of delegated power for the user. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType); /** * @dev Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external virtual; /** * @dev Delegates all governance powers to a delegatee. * * @param delegatee The user to which the power will be delegated. */ function delegate(address delegatee) external virtual; /** * @dev Returns the delegatee of an user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external view virtual returns (address); /** * @dev Returns the current delegated power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external view virtual returns (uint256); /** * @dev Returns the delegated power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external view virtual returns (uint256); } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SafeMath } from '../../../dependencies/open-zeppelin/SafeMath.sol'; import { SM1Snapshots } from './SM1Snapshots.sol'; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1ExchangeRate * @author dYdX * * @dev Performs math using the exchange rate, which converts between underlying units of the token * that was staked (e.g. STAKED_TOKEN.balanceOf(account)), and staked units, used by this contract * for all staked balances (e.g. this.balanceOf(account)). * * OVERVIEW: * * The exchange rate is stored as a multiple of EXCHANGE_RATE_BASE, and represents the number of * staked balance units that each unit of underlying token is worth. Before any slashes have * occurred, the exchange rate is equal to one. The exchange rate can increase with each slash, * indicating that staked balances are becoming less and less valuable, per unit, relative to the * underlying token. * * AVOIDING OVERFLOW AND UNDERFLOW: * * Staked balances are represented internally as uint240, so the result of an operation returning * a staked balances must return a value less than 2^240. Intermediate values in calcuations are * represented as uint256, so all operations within a calculation must return values under 2^256. * * In the functions below operating on the exchange rate, we are strategic in our choice of the * order of multiplication and division operations, in order to avoid both overflow and underflow. * * We use the following assumptions and principles to implement this module: * - (ASSUMPTION) An amount denoted in underlying token units is never greater than 10^28. * - If the exchange rate is greater than 10^46, then we may perform division on the exchange * rate before performing multiplication, provided that the denominator is not greater * than 10^28 (to ensure a result with at least 18 decimals of precision). Specifically, * we use EXCHANGE_RATE_MAY_OVERFLOW as the cutoff, which is a number greater than 10^46. * - Since staked balances are stored as uint240, we cap the exchange rate to ensure that a * staked balance can never overflow (using the assumption above). */ abstract contract SM1ExchangeRate is SM1Snapshots, SM1Storage { using SafeMath for uint256; // ============ Constants ============ /// @notice The assumed upper bound on the total supply of the staked token. uint256 public constant MAX_UNDERLYING_BALANCE = 1e28; /// @notice Base unit used to represent the exchange rate, for additional precision. uint256 public constant EXCHANGE_RATE_BASE = 1e18; /// @notice Cutoff where an exchange rate may overflow after multiplying by an underlying balance. /// @dev Approximately 1.2e49 uint256 public constant EXCHANGE_RATE_MAY_OVERFLOW = (2 ** 256 - 1) / MAX_UNDERLYING_BALANCE; /// @notice Cutoff where a stake amount may overflow after multiplying by EXCHANGE_RATE_BASE. /// @dev Approximately 1.2e59 uint256 public constant STAKE_AMOUNT_MAY_OVERFLOW = (2 ** 256 - 1) / EXCHANGE_RATE_BASE; /// @notice Max exchange rate. /// @dev Approximately 1.8e62 uint256 public constant MAX_EXCHANGE_RATE = ( ((2 ** 240 - 1) / MAX_UNDERLYING_BALANCE) * EXCHANGE_RATE_BASE ); // ============ Initializer ============ function __SM1ExchangeRate_init() internal { _EXCHANGE_RATE_ = EXCHANGE_RATE_BASE; } function stakeAmountFromUnderlyingAmount( uint256 underlyingAmount ) internal view returns (uint256) { uint256 exchangeRate = _EXCHANGE_RATE_; if (exchangeRate > EXCHANGE_RATE_MAY_OVERFLOW) { uint256 exchangeRateUnbased = exchangeRate.div(EXCHANGE_RATE_BASE); return underlyingAmount.mul(exchangeRateUnbased); } else { return underlyingAmount.mul(exchangeRate).div(EXCHANGE_RATE_BASE); } } function underlyingAmountFromStakeAmount( uint256 stakeAmount ) internal view returns (uint256) { return underlyingAmountFromStakeAmountWithExchangeRate(stakeAmount, _EXCHANGE_RATE_); } function underlyingAmountFromStakeAmountWithExchangeRate( uint256 stakeAmount, uint256 exchangeRate ) internal pure returns (uint256) { if (stakeAmount > STAKE_AMOUNT_MAY_OVERFLOW) { // Note that this case implies that exchangeRate > EXCHANGE_RATE_MAY_OVERFLOW. uint256 exchangeRateUnbased = exchangeRate.div(EXCHANGE_RATE_BASE); return stakeAmount.div(exchangeRateUnbased); } else { return stakeAmount.mul(EXCHANGE_RATE_BASE).div(exchangeRate); } } function updateExchangeRate( uint256 numerator, uint256 denominator ) internal returns (uint256) { uint256 oldExchangeRate = _EXCHANGE_RATE_; // Avoid overflow. // Note that the numerator and denominator are both denominated in underlying token units. uint256 newExchangeRate; if (oldExchangeRate > EXCHANGE_RATE_MAY_OVERFLOW) { newExchangeRate = oldExchangeRate.div(denominator).mul(numerator); } else { newExchangeRate = oldExchangeRate.mul(numerator).div(denominator); } require( newExchangeRate <= MAX_EXCHANGE_RATE, 'SM1ExchangeRate: Max exchange rate exceeded' ); _EXCHANGE_RATE_SNAPSHOT_COUNT_ = _writeSnapshot( _EXCHANGE_RATE_SNAPSHOTS_, _EXCHANGE_RATE_SNAPSHOT_COUNT_, newExchangeRate ); _EXCHANGE_RATE_ = newExchangeRate; return newExchangeRate; } } // SPDX-License-Identifier: AGPL-3.0 pragma solidity 0.7.5; pragma abicoder v2; import { SM1Types } from '../lib/SM1Types.sol'; import { SM1Storage } from './SM1Storage.sol'; /** * @title SM1Snapshots * @author dYdX * * @dev Handles storage and retrieval of historical values by block number. * * Note that the snapshot stored at a given block number represents the value as of the end of * that block. */ abstract contract SM1Snapshots { /** * @dev Writes a snapshot of a value at the current block. * * @param snapshots Storage mapping from snapshot index to snapshot struct. * @param snapshotCount The total number of snapshots in the provided mapping. * @param newValue The new value to snapshot at the current block. * * @return The new snapshot count. */ function _writeSnapshot( mapping(uint256 => SM1Types.Snapshot) storage snapshots, uint256 snapshotCount, uint256 newValue ) internal returns (uint256) { uint256 currentBlock = block.number; if ( snapshotCount != 0 && snapshots[snapshotCount - 1].blockNumber == currentBlock ) { // If there was a previous snapshot for this block, overwrite it. snapshots[snapshotCount - 1].value = newValue; return snapshotCount; } else { snapshots[snapshotCount] = SM1Types.Snapshot(currentBlock, newValue); return snapshotCount + 1; } } /** * @dev Search for the snapshot value at a given block. Uses binary search. * * Reverts if `blockNumber` is greater than the current block number. * * @param snapshots Storage mapping from snapshot index to snapshot struct. * @param snapshotCount The total number of snapshots in the provided mapping. * @param blockNumber The block number to search for. * @param initialValue The value to return if `blockNumber` is before the earliest snapshot. * * @return The snapshot value at the specified block number. */ function _findValueAtBlock( mapping(uint256 => SM1Types.Snapshot) storage snapshots, uint256 snapshotCount, uint256 blockNumber, uint256 initialValue ) internal view returns (uint256) { require( blockNumber <= block.number, 'SM1Snapshots: INVALID_BLOCK_NUMBER' ); if (snapshotCount == 0) { return initialValue; } // Check earliest snapshot. if (blockNumber < snapshots[0].blockNumber) { return initialValue; } // Check latest snapshot. if (blockNumber >= snapshots[snapshotCount - 1].blockNumber) { return snapshots[snapshotCount - 1].value; } uint256 lower = 0; uint256 upper = snapshotCount - 1; while (upper > lower) { uint256 center = upper - (upper - lower) / 2; // Ceil, avoiding overflow. SM1Types.Snapshot memory snapshot = snapshots[center]; if (snapshot.blockNumber == blockNumber) { return snapshot.value; } else if (snapshot.blockNumber < blockNumber) { lower = center; } else { upper = center - 1; } } return snapshots[lower].value; } }
File 6 of 6: DydxToken
// SPDX-License-Identifier: AGPL-3.0 // File contracts/dependencies/open-zeppelin/Context.sol pragma solidity 0.7.5; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File contracts/dependencies/open-zeppelin/IERC20.sol pragma solidity 0.7.5; /** * @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); } // File contracts/dependencies/open-zeppelin/SafeMath.sol pragma solidity 0.7.5; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, 'SafeMath: addition overflow'); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File contracts/dependencies/open-zeppelin/Address.sol pragma solidity 0.7.5; /** * @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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(''); require(success, 'Address: unable to send value, recipient may have reverted'); } } // File contracts/dependencies/open-zeppelin/ERC20.sol pragma solidity ^0.7.5; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string internal _name; string internal _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() virtual public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() virtual public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() virtual public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File contracts/dependencies/open-zeppelin/Ownable.sol pragma solidity 0.7.5; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), 'Ownable: caller is not the owner'); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), 'Ownable: new owner is the zero address'); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File contracts/interfaces/IGovernancePowerDelegationERC20.sol pragma solidity 0.7.5; interface IGovernancePowerDelegationERC20 { enum DelegationType { VOTING_POWER, PROPOSITION_POWER } /** * @dev Emitted when a user delegates governance power to another user. * * @param delegator The delegator. * @param delegatee The delegatee. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegateChanged( address indexed delegator, address indexed delegatee, DelegationType delegationType ); /** * @dev Emitted when an action changes the delegated power of a user. * * @param user The user whose delegated power has changed. * @param amount The new amount of delegated power for the user. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType); /** * @dev Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType(address delegatee, DelegationType delegationType) external virtual; /** * @dev Delegates all governance powers to a delegatee. * * @param delegatee The user to which the power will be delegated. */ function delegate(address delegatee) external virtual; /** * @dev Returns the delegatee of an user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType(address delegator, DelegationType delegationType) external view virtual returns (address); /** * @dev Returns the current delegated power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent(address user, DelegationType delegationType) external view virtual returns (uint256); /** * @dev Returns the delegated power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external view virtual returns (uint256); } // File contracts/governance/token/GovernancePowerDelegationERC20Mixin.sol pragma solidity 0.7.5; /** * @title GovernancePowerDelegationERC20Mixin * @author dYdX * * @dev Provides support for two types of governance powers, both endowed by the governance * token, and separately delegatable. Provides functions for delegation and for querying a user's * power at a certain block number. */ abstract contract GovernancePowerDelegationERC20Mixin is ERC20, IGovernancePowerDelegationERC20 { using SafeMath for uint256; // ============ Constants ============ /// @notice EIP-712 typehash for delegation by signature of a specific governance power type. bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256( 'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)' ); /// @notice EIP-712 typehash for delegation by signature of all governance powers. bytes32 public constant DELEGATE_TYPEHASH = keccak256( 'Delegate(address delegatee,uint256 nonce,uint256 expiry)' ); // ============ Structs ============ /// @dev Snapshot of a value on a specific block, used to track voting power for proposals. struct Snapshot { uint128 blockNumber; uint128 value; } // ============ External Functions ============ /** * @notice Delegates a specific governance power to a delegatee. * * @param delegatee The address to delegate power to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function delegateByType( address delegatee, DelegationType delegationType ) external override { _delegateByType(msg.sender, delegatee, delegationType); } /** * @notice Delegates all governance powers to a delegatee. * * @param delegatee The address to delegate power to. */ function delegate( address delegatee ) external override { _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER); _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER); } /** * @notice Returns the delegatee of a user. * * @param delegator The address of the delegator. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). */ function getDelegateeByType( address delegator, DelegationType delegationType ) external override view returns (address) { (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); return _getDelegatee(delegator, delegates); } /** * @notice Returns the current power of a user. The current power is the power delegated * at the time of the last snapshot. * * @param user The user whose power to query. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerCurrent( address user, DelegationType delegationType ) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number); } /** * @notice Returns the power of a user at a certain block. * * @param user The user whose power to query. * @param blockNumber The block number at which to get the user's power. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function getPowerAtBlock( address user, uint256 blockNumber, DelegationType delegationType ) external override view returns (uint256) { ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber); } // ============ Internal Functions ============ /** * @dev Delegates one specific power to a delegatee. * * @param delegator The user whose power to delegate. * @param delegatee The address to delegate power to. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _delegateByType( address delegator, address delegatee, DelegationType delegationType ) internal { require( delegatee != address(0), 'INVALID_DELEGATEE' ); (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType); uint256 delegatorBalance = balanceOf(delegator); address previousDelegatee = _getDelegatee(delegator, delegates); delegates[delegator] = delegatee; _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType); emit DelegateChanged(delegator, delegatee, delegationType); } /** * @dev Moves power from one user to another. * * @param from The user from which delegated power is moved. * @param to The user that will receive the delegated power. * @param amount The amount of power to be moved. * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _moveDelegatesByType( address from, address to, uint256 amount, DelegationType delegationType ) internal { if (from == to) { return; } ( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, // delegates ) = _getDelegationDataByType(delegationType); if (from != address(0)) { uint256 previous = 0; uint256 fromSnapshotsCount = snapshotsCounts[from]; if (fromSnapshotsCount != 0) { previous = snapshots[from][fromSnapshotsCount - 1].value; } else { previous = balanceOf(from); } uint256 newAmount = previous.sub(amount); _writeSnapshot( snapshots, snapshotsCounts, from, uint128(newAmount) ); emit DelegatedPowerChanged(from, newAmount, delegationType); } if (to != address(0)) { uint256 previous = 0; uint256 toSnapshotsCount = snapshotsCounts[to]; if (toSnapshotsCount != 0) { previous = snapshots[to][toSnapshotsCount - 1].value; } else { previous = balanceOf(to); } uint256 newAmount = previous.add(amount); _writeSnapshot( snapshots, snapshotsCounts, to, uint128(newAmount) ); emit DelegatedPowerChanged(to, newAmount, delegationType); } } /** * @dev Searches for a balance snapshot by block number using binary search. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param user The user for which the snapshot is being searched. * @param blockNumber The block number being searched. */ function _searchByBlockNumber( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address user, uint256 blockNumber ) internal view returns (uint256) { require( blockNumber <= block.number, 'INVALID_BLOCK_NUMBER' ); uint256 snapshotsCount = snapshotsCounts[user]; if (snapshotsCount == 0) { return balanceOf(user); } // First check most recent balance if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) { return snapshots[user][snapshotsCount - 1].value; } // Next check implicit zero balance if (snapshots[user][0].blockNumber > blockNumber) { return 0; } uint256 lower = 0; uint256 upper = snapshotsCount - 1; while (upper > lower) { uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Snapshot memory snapshot = snapshots[user][center]; if (snapshot.blockNumber == blockNumber) { return snapshot.value; } else if (snapshot.blockNumber < blockNumber) { lower = center; } else { upper = center - 1; } } return snapshots[user][lower].value; } /** * @dev Returns delegation data (snapshot, snapshotsCount, delegates) by delegation type. * * Note: This mixin contract does not itself define any storage, and we require the inheriting * contract to implement this method to provide access to the relevant mappings in storage. * This pattern was implemented by Aave for legacy reasons and we have decided not to change it. * * @param delegationType The type of power (VOTING_POWER, PROPOSITION_POWER). */ function _getDelegationDataByType( DelegationType delegationType ) internal virtual view returns ( mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots mapping(address => uint256) storage, // snapshotsCount mapping(address => address) storage // delegates ); /** * @dev Writes a snapshot of a user's token/power balance. * * @param snapshots The mapping of snapshots by user. * @param snapshotsCounts The mapping of the number of snapshots by user. * @param owner The user whose power to snapshot. * @param newValue The new balance to snapshot at the current block. */ function _writeSnapshot( mapping(address => mapping(uint256 => Snapshot)) storage snapshots, mapping(address => uint256) storage snapshotsCounts, address owner, uint128 newValue ) internal { uint128 currentBlock = uint128(block.number); uint256 ownerSnapshotsCount = snapshotsCounts[owner]; mapping(uint256 => Snapshot) storage ownerSnapshots = snapshots[owner]; if ( ownerSnapshotsCount != 0 && ownerSnapshots[ownerSnapshotsCount - 1].blockNumber == currentBlock ) { // Doing multiple operations in the same block ownerSnapshots[ownerSnapshotsCount - 1].value = newValue; } else { ownerSnapshots[ownerSnapshotsCount] = Snapshot(currentBlock, newValue); snapshotsCounts[owner] = ownerSnapshotsCount + 1; } } /** * @dev Returns the delegatee of a user. If a user never performed any delegation, their * delegated address will be 0x0, in which case we return the user's own address. * * @param delegator The address of the user for which return the delegatee. * @param delegates The mapping of delegates for a particular type of delegation. */ function _getDelegatee( address delegator, mapping(address => address) storage delegates ) internal view returns (address) { address previousDelegatee = delegates[delegator]; if (previousDelegatee == address(0)) { return delegator; } return previousDelegatee; } } // File contracts/governance/token/DydxToken.sol pragma solidity 0.7.5; /** * @title DydxToken * @author dYdX * * @notice The dYdX governance token. */ contract DydxToken is GovernancePowerDelegationERC20Mixin, Ownable { using SafeMath for uint256; // ============ Events ============ /** * @dev Emitted when an address has been added to or removed from the token transfer allowlist. * * @param account Address that was added to or removed from the token transfer allowlist. * @param isAllowed True if the address was added to the allowlist, false if removed. */ event TransferAllowlistUpdated( address account, bool isAllowed ); /** * @dev Emitted when the transfer restriction timestamp is reassigned. * * @param transfersRestrictedBefore The new timestamp on and after which non-allowlisted * transfers may occur. */ event TransfersRestrictedBeforeUpdated( uint256 transfersRestrictedBefore ); // ============ Constants ============ string internal constant NAME = 'dYdX'; string internal constant SYMBOL = 'DYDX'; uint256 public constant INITIAL_SUPPLY = 1_000_000_000 ether; bytes32 public immutable DOMAIN_SEPARATOR; bytes public constant EIP712_VERSION = '1'; bytes32 public constant EIP712_DOMAIN = keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ); bytes32 public constant PERMIT_TYPEHASH = keccak256( 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)' ); /// @notice Minimum time between mints. uint256 public constant MINT_MIN_INTERVAL = 365 days; /// @notice Cap on the percentage of the total supply that can be minted at each mint. /// Denominated in percentage points (units out of 100). uint256 public immutable MINT_MAX_PERCENT; /// @notice The timestamp on and after which the transfer restriction must be lifted. uint256 public immutable TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN; // ============ Storage ============ /// @dev Mapping from (owner) => (next valid nonce) for EIP-712 signatures. mapping(address => uint256) internal _nonces; mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots; mapping(address => uint256) public _votingSnapshotsCounts; mapping(address => address) public _votingDelegates; mapping(address => mapping(uint256 => Snapshot)) public _propositionPowerSnapshots; mapping(address => uint256) public _propositionPowerSnapshotsCounts; mapping(address => address) public _propositionPowerDelegates; /// @notice Snapshots of the token total supply, at each block where the total supply has changed. mapping(uint256 => Snapshot) public _totalSupplySnapshots; /// @notice Number of snapshots of the token total supply. uint256 public _totalSupplySnapshotsCount; /// @notice Allowlist of addresses which may send or receive tokens while transfers are /// otherwise restricted. mapping(address => bool) public _tokenTransferAllowlist; /// @notice The timestamp on and after which minting may occur. uint256 public _mintingRestrictedBefore; /// @notice The timestamp on and after which non-allowlisted transfers may occur. uint256 public _transfersRestrictedBefore; // ============ Constructor ============ /** * @notice Constructor. * * @param distributor The address which will receive the initial supply of tokens. * @param transfersRestrictedBefore Timestamp, before which transfers are restricted unless the * origin or destination address is in the allowlist. * @param transferRestrictionLiftedNoLaterThan Timestamp, which is the maximum timestamp that transfer * restrictions can be extended to. * @param mintingRestrictedBefore Timestamp, before which minting is not allowed. * @param mintMaxPercent Cap on the percentage of the total supply that can be minted at * each mint. */ constructor( address distributor, uint256 transfersRestrictedBefore, uint256 transferRestrictionLiftedNoLaterThan, uint256 mintingRestrictedBefore, uint256 mintMaxPercent ) ERC20(NAME, SYMBOL) { uint256 chainId; // solium-disable-next-line assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, keccak256(bytes(NAME)), keccak256(bytes(EIP712_VERSION)), chainId, address(this) ) ); // Validate and set parameters. require( transfersRestrictedBefore > block.timestamp, 'TRANSFERS_RESTRICTED_BEFORE_TOO_EARLY' ); require( transfersRestrictedBefore <= transferRestrictionLiftedNoLaterThan, 'MAX_TRANSFER_RESTRICTION_TOO_EARLY' ); require( mintingRestrictedBefore > block.timestamp, 'MINTING_RESTRICTED_BEFORE_TOO_EARLY' ); _transfersRestrictedBefore = transfersRestrictedBefore; TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN = transferRestrictionLiftedNoLaterThan; _mintingRestrictedBefore = mintingRestrictedBefore; MINT_MAX_PERCENT = mintMaxPercent; // Mint the initial supply. _mint(distributor, INITIAL_SUPPLY); emit TransfersRestrictedBeforeUpdated(transfersRestrictedBefore); } // ============ Other Functions ============ /** * @notice Adds addresses to the token transfer allowlist. Reverts if any of the addresses * already exist in the allowlist. Only callable by owner. * * @param addressesToAdd Addresses to add to the token transfer allowlist. */ function addToTokenTransferAllowlist( address[] calldata addressesToAdd ) external onlyOwner { for (uint256 i = 0; i < addressesToAdd.length; i++) { require( !_tokenTransferAllowlist[addressesToAdd[i]], 'ADDRESS_EXISTS_IN_TRANSFER_ALLOWLIST' ); _tokenTransferAllowlist[addressesToAdd[i]] = true; emit TransferAllowlistUpdated(addressesToAdd[i], true); } } /** * @notice Removes addresses from the token transfer allowlist. Reverts if any of the addresses * don't exist in the allowlist. Only callable by owner. * * @param addressesToRemove Addresses to remove from the token transfer allowlist. */ function removeFromTokenTransferAllowlist( address[] calldata addressesToRemove ) external onlyOwner { for (uint256 i = 0; i < addressesToRemove.length; i++) { require( _tokenTransferAllowlist[addressesToRemove[i]], 'ADDRESS_DOES_NOT_EXIST_IN_TRANSFER_ALLOWLIST' ); _tokenTransferAllowlist[addressesToRemove[i]] = false; emit TransferAllowlistUpdated(addressesToRemove[i], false); } } /** * @notice Updates the transfer restriction. Reverts if the transfer restriction has already passed, * the new transfer restriction is earlier than the previous one, or the new transfer restriction is * after the maximum transfer restriction. * * @param transfersRestrictedBefore The timestamp on and after which non-allowlisted transfers may occur. */ function updateTransfersRestrictedBefore( uint256 transfersRestrictedBefore ) external onlyOwner { uint256 previousTransfersRestrictedBefore = _transfersRestrictedBefore; require( block.timestamp < previousTransfersRestrictedBefore, 'TRANSFER_RESTRICTION_ENDED' ); require( previousTransfersRestrictedBefore <= transfersRestrictedBefore, 'NEW_TRANSFER_RESTRICTION_TOO_EARLY' ); require( transfersRestrictedBefore <= TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN, 'AFTER_MAX_TRANSFER_RESTRICTION' ); _transfersRestrictedBefore = transfersRestrictedBefore; emit TransfersRestrictedBeforeUpdated(transfersRestrictedBefore); } /** * @notice Mint new tokens. Only callable by owner after the required time period has elapsed. * * @param recipient The address to receive minted tokens. * @param amount The number of tokens to mint. */ function mint( address recipient, uint256 amount ) external onlyOwner { require( block.timestamp >= _mintingRestrictedBefore, 'MINT_TOO_EARLY' ); require( amount <= totalSupply().mul(MINT_MAX_PERCENT).div(100), 'MAX_MINT_EXCEEDED' ); // Update the next allowed minting time. _mintingRestrictedBefore = block.timestamp.add(MINT_MIN_INTERVAL); // Mint the amount. _mint(recipient, amount); } /** * @notice Implements the permit function as specified in EIP-2612. * * @param owner Address of the token owner. * @param spender Address of the spender. * @param value Amount of allowance. * @param deadline Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require( owner != address(0), 'INVALID_OWNER' ); require( block.timestamp <= deadline, 'INVALID_EXPIRATION' ); uint256 currentValidNonce = _nonces[owner]; bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) ) ); require( owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE' ); _nonces[owner] = currentValidNonce.add(1); _approve(owner, spender, value); } /** * @notice Get the next valid nonce for EIP-712 signatures. * * This nonce should be used when signing for any of the following functions: * - permit() * - delegateByTypeBySig() * - delegateBySig() */ function nonces( address owner ) external view returns (uint256) { return _nonces[owner]; } function transfer( address recipient, uint256 amount ) public override returns (bool) { _requireTransferAllowed(_msgSender(), recipient); return super.transfer(recipient, amount); } function transferFrom( address sender, address recipient, uint256 amount ) public override returns (bool) { _requireTransferAllowed(sender, recipient); return super.transferFrom(sender, recipient, amount); } /** * @dev Override _mint() to write a snapshot whenever the total supply changes. * * These snapshots are intended to be used by the governance strategy. * * Note that the ERC20 _burn() function is never used. If desired, an official burn mechanism * could be implemented external to this contract, and accounted for in the governance strategy. */ function _mint( address account, uint256 amount ) internal override { super._mint(account, amount); uint256 snapshotsCount = _totalSupplySnapshotsCount; uint128 currentBlock = uint128(block.number); uint128 newValue = uint128(totalSupply()); // Note: There is no special case for the total supply being updated multiple times in the same // block. That should never occur. _totalSupplySnapshots[snapshotsCount] = Snapshot(currentBlock, newValue); _totalSupplySnapshotsCount = snapshotsCount.add(1); } function _requireTransferAllowed( address sender, address recipient ) view internal { // Compare against the constant `TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN` first // to avoid additional gas costs from reading from storage. if ( block.timestamp < TRANSFER_RESTRICTION_LIFTED_NO_LATER_THAN && block.timestamp < _transfersRestrictedBefore ) { // While transfers are restricted, a transfer is permitted if either the sender or the // recipient is on the allowlist. require( _tokenTransferAllowlist[sender] || _tokenTransferAllowlist[recipient], 'NON_ALLOWLIST_TRANSFERS_DISABLED' ); } } /** * @dev Writes a snapshot before any transfer operation, including: _transfer, _mint and _burn. * - On _transfer, it writes snapshots for both 'from' and 'to'. * - On _mint, only for `to`. * - On _burn, only for `from`. * * @param from The sender. * @param to The recipient. * @param amount The amount being transfered. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal override { address votingFromDelegatee = _getDelegatee(from, _votingDelegates); address votingToDelegatee = _getDelegatee(to, _votingDelegates); _moveDelegatesByType( votingFromDelegatee, votingToDelegatee, amount, DelegationType.VOTING_POWER ); address propPowerFromDelegatee = _getDelegatee(from, _propositionPowerDelegates); address propPowerToDelegatee = _getDelegatee(to, _propositionPowerDelegates); _moveDelegatesByType( propPowerFromDelegatee, propPowerToDelegatee, amount, DelegationType.PROPOSITION_POWER ); } function _getDelegationDataByType( DelegationType delegationType ) internal override view returns ( mapping(address => mapping(uint256 => Snapshot)) storage, // snapshots mapping(address => uint256) storage, // snapshots count mapping(address => address) storage // delegatees list ) { if (delegationType == DelegationType.VOTING_POWER) { return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates); } else { return ( _propositionPowerSnapshots, _propositionPowerSnapshotsCounts, _propositionPowerDelegates ); } } /** * @dev Delegates specific governance power from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param delegationType The type of delegation (VOTING_POWER, PROPOSITION_POWER). * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateByTypeBySig( address delegatee, DelegationType delegationType, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) public { bytes32 structHash = keccak256( abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry) ); bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(digest, v, r, s); require( signer != address(0), 'INVALID_SIGNATURE' ); require( nonce == _nonces[signer]++, 'INVALID_NONCE' ); require( block.timestamp <= expiry, 'INVALID_EXPIRATION' ); _delegateByType(signer, delegatee, delegationType); } /** * @dev Delegates both governance powers from signer to `delegatee` using an EIP-712 signature. * * @param delegatee The address to delegate votes to. * @param nonce The signer's nonce for EIP-712 signatures on this contract. * @param expiry Expiration timestamp for the signature. * @param v Signature param. * @param r Signature param. * @param s Signature param. */ function delegateBySig( address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) public { bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(digest, v, r, s); require( signer != address(0), 'INVALID_SIGNATURE' ); require( nonce == _nonces[signer]++, 'INVALID_NONCE' ); require( block.timestamp <= expiry, 'INVALID_EXPIRATION' ); _delegateByType(signer, delegatee, DelegationType.VOTING_POWER); _delegateByType(signer, delegatee, DelegationType.PROPOSITION_POWER); } }