-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added tests and preliminary versioning
- Loading branch information
1 parent
48490d0
commit 8d856cc
Showing
20 changed files
with
682 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
340 changes: 340 additions & 0 deletions
340
packages/contracts/src/core/dao/previous_versions/DAO_v1_0_0.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
pragma solidity 0.8.17; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165StorageUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; | ||
import "@openzeppelin/contracts/interfaces/IERC1271.sol"; | ||
|
||
import {PermissionManager} from "../../permission/PermissionManager.sol"; | ||
import {CallbackHandler} from "../../utils/CallbackHandler.sol"; | ||
import {hasBit, flipBit} from "../../utils/BitMap.sol"; | ||
import {IEIP4824} from "../IEIP4824.sol"; | ||
import {IDAO_v1_0_0} from "./IDAO_v1_0_0.sol"; | ||
|
||
/// @title DAO v1.0.0 | ||
/// @author Aragon Association - 2021-2023 | ||
/// @notice This contract is the entry point to the Aragon DAO framework and provides our users a simple and easy to use public interface. | ||
/// @dev Public API of the Aragon DAO framework. | ||
contract DAO_v1_0_0 is | ||
IEIP4824, | ||
Initializable, | ||
IERC1271, | ||
ERC165StorageUpgradeable, | ||
IDAO_v1_0_0, | ||
UUPSUpgradeable, | ||
PermissionManager, | ||
CallbackHandler | ||
{ | ||
using SafeERC20Upgradeable for IERC20Upgradeable; | ||
using AddressUpgradeable for address; | ||
|
||
/// @notice The ID of the permission required to call the `execute` function. | ||
bytes32 public constant EXECUTE_PERMISSION_ID = keccak256("EXECUTE_PERMISSION"); | ||
|
||
/// @notice The ID of the permission required to call the `_authorizeUpgrade` function. | ||
bytes32 public constant UPGRADE_DAO_PERMISSION_ID = keccak256("UPGRADE_DAO_PERMISSION"); | ||
|
||
/// @notice The ID of the permission required to call the `setMetadata` function. | ||
bytes32 public constant SET_METADATA_PERMISSION_ID = keccak256("SET_METADATA_PERMISSION"); | ||
|
||
/// @notice The ID of the permission required to call the `setTrustedForwarder` function. | ||
bytes32 public constant SET_TRUSTED_FORWARDER_PERMISSION_ID = | ||
keccak256("SET_TRUSTED_FORWARDER_PERMISSION"); | ||
|
||
/// @notice The ID of the permission required to call the `setSignatureValidator` function. | ||
bytes32 public constant SET_SIGNATURE_VALIDATOR_PERMISSION_ID = | ||
keccak256("SET_SIGNATURE_VALIDATOR_PERMISSION"); | ||
|
||
/// @notice The ID of the permission required to call the `registerStandardCallback` function. | ||
bytes32 public constant REGISTER_STANDARD_CALLBACK_PERMISSION_ID = | ||
keccak256("REGISTER_STANDARD_CALLBACK_PERMISSION"); | ||
|
||
/// @notice The internal constant storing the maximal action array length. | ||
uint256 internal constant MAX_ACTIONS = 256; | ||
|
||
/// @notice The [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signature validator contract. | ||
IERC1271 public signatureValidator; | ||
|
||
/// @notice The address of the trusted forwarder verifying meta transactions. | ||
address private trustedForwarder; | ||
|
||
/// @notice The [EIP-4824](https://eips.ethereum.org/EIPS/eip-4824) DAO uri. | ||
string private _daoURI; | ||
|
||
/// @notice Thrown if the action array length is larger than `MAX_ACTIONS`. | ||
error TooManyActions(); | ||
|
||
/// @notice Thrown if action execution has failed. | ||
/// @param index The index of the action in the action array that failed. | ||
error ActionFailed(uint256 index); | ||
|
||
/// @notice Thrown if the deposit amount is zero. | ||
error ZeroAmount(); | ||
|
||
/// @notice Thrown if there is a mismatch between the expected and actually deposited amount of native tokens. | ||
/// @param expected The expected native token amount. | ||
/// @param actual The actual native token amount deposited. | ||
error NativeTokenDepositAmountMismatch(uint256 expected, uint256 actual); | ||
|
||
/// @notice Emitted when a new DAO uri is set. | ||
/// @param daoURI The new uri. | ||
event NewURI(string daoURI); | ||
|
||
/// @notice Disables the initializers on the implementation contract to prevent it from being left uninitialized. | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
/// @notice Initializes the DAO by | ||
/// - registering the [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID | ||
/// - setting the trusted forwarder for meta transactions | ||
/// - giving the `ROOT_PERMISSION_ID` permission to the initial owner (that should be revoked and transferred to the DAO after setup). | ||
/// @dev This method is required to support [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822). | ||
/// @param _metadata IPFS hash that points to all the metadata (logo, description, tags, etc.) of a DAO. | ||
/// @param _initialOwner The initial owner of the DAO having the `ROOT_PERMISSION_ID` permission. | ||
/// @param _trustedForwarder The trusted forwarder responsible for verifying meta transactions. | ||
function initialize( | ||
bytes calldata _metadata, | ||
address _initialOwner, | ||
address _trustedForwarder, | ||
string calldata daoURI_ | ||
) external initializer { | ||
_registerInterface(type(IDAO_v1_0_0).interfaceId); | ||
_registerInterface(type(IERC1271).interfaceId); | ||
_registerInterface(type(IEIP4824).interfaceId); | ||
_registerTokenInterfaces(); | ||
|
||
_setMetadata(_metadata); | ||
_setTrustedForwarder(_trustedForwarder); | ||
_setDaoURI(daoURI_); | ||
__PermissionManager_init(_initialOwner); | ||
} | ||
|
||
/// @inheritdoc PermissionManager | ||
function isPermissionRestrictedForAnyAddr( | ||
bytes32 _permissionId | ||
) internal pure override returns (bool) { | ||
return | ||
_permissionId == EXECUTE_PERMISSION_ID || | ||
_permissionId == UPGRADE_DAO_PERMISSION_ID || | ||
_permissionId == SET_METADATA_PERMISSION_ID || | ||
_permissionId == SET_TRUSTED_FORWARDER_PERMISSION_ID || | ||
_permissionId == SET_SIGNATURE_VALIDATOR_PERMISSION_ID || | ||
_permissionId == REGISTER_STANDARD_CALLBACK_PERMISSION_ID; | ||
} | ||
|
||
/// @notice Internal method authorizing the upgrade of the contract via the [upgradeabilty mechanism for UUPS proxies](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) (see [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822)). | ||
/// @dev The caller must have the `UPGRADE_DAO_PERMISSION_ID` permission. | ||
function _authorizeUpgrade(address) internal virtual override auth(UPGRADE_DAO_PERMISSION_ID) {} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function setTrustedForwarder( | ||
address _newTrustedForwarder | ||
) external override auth(SET_TRUSTED_FORWARDER_PERMISSION_ID) { | ||
_setTrustedForwarder(_newTrustedForwarder); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function getTrustedForwarder() external view virtual override returns (address) { | ||
return trustedForwarder; | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function hasPermission( | ||
address _where, | ||
address _who, | ||
bytes32 _permissionId, | ||
bytes memory _data | ||
) external view override returns (bool) { | ||
return isGranted(_where, _who, _permissionId, _data); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function setMetadata( | ||
bytes calldata _metadata | ||
) external override auth(SET_METADATA_PERMISSION_ID) { | ||
_setMetadata(_metadata); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function execute( | ||
bytes32 _callId, | ||
Action[] calldata _actions, | ||
uint256 _allowFailureMap | ||
) | ||
external | ||
override | ||
auth(EXECUTE_PERMISSION_ID) | ||
returns (bytes[] memory execResults, uint256 failureMap) | ||
{ | ||
if (_actions.length > MAX_ACTIONS) { | ||
revert TooManyActions(); | ||
} | ||
|
||
execResults = new bytes[](_actions.length); | ||
|
||
for (uint256 i = 0; i < _actions.length; ) { | ||
address to = _actions[i].to; | ||
(bool success, bytes memory response) = to.call{value: _actions[i].value}( | ||
_actions[i].data | ||
); | ||
|
||
if (!success) { | ||
// If the call failed and wasn't allowed in allowFailureMap, revert. | ||
if (!hasBit(_allowFailureMap, uint8(i))) { | ||
revert ActionFailed(i); | ||
} | ||
|
||
// If the call failed, but was allowed in allowFailureMap, store that | ||
// this specific action has actually failed. | ||
failureMap = flipBit(failureMap, uint8(i)); | ||
} | ||
|
||
execResults[i] = response; | ||
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
|
||
emit Executed({ | ||
actor: msg.sender, | ||
callId: _callId, | ||
actions: _actions, | ||
failureMap: failureMap, | ||
execResults: execResults | ||
}); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function deposit( | ||
address _token, | ||
uint256 _amount, | ||
string calldata _reference | ||
) external payable override { | ||
if (_amount == 0) revert ZeroAmount(); | ||
|
||
if (_token == address(0)) { | ||
if (msg.value != _amount) | ||
revert NativeTokenDepositAmountMismatch({expected: _amount, actual: msg.value}); | ||
} else { | ||
if (msg.value != 0) | ||
revert NativeTokenDepositAmountMismatch({expected: 0, actual: msg.value}); | ||
|
||
IERC20Upgradeable(_token).safeTransferFrom(msg.sender, address(this), _amount); | ||
} | ||
|
||
emit Deposited(msg.sender, _token, _amount, _reference); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function setSignatureValidator( | ||
address _signatureValidator | ||
) external override auth(SET_SIGNATURE_VALIDATOR_PERMISSION_ID) { | ||
signatureValidator = IERC1271(_signatureValidator); | ||
|
||
emit SignatureValidatorSet({signatureValidator: _signatureValidator}); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function isValidSignature( | ||
bytes32 _hash, | ||
bytes memory _signature | ||
) external view override(IDAO_v1_0_0, IERC1271) returns (bytes4) { | ||
if (address(signatureValidator) == address(0)) { | ||
// Return the invalid magic number | ||
return bytes4(0); | ||
} | ||
// Forward the call to the set signature validator contract | ||
return signatureValidator.isValidSignature(_hash, _signature); | ||
} | ||
|
||
/// @notice Emits the `NativeTokenDeposited` event to track native token deposits that weren't made via the deposit method. | ||
/// @dev This call is bound by the gas limitations for `send`/`transfer` calls introduced by EIP-2929. | ||
/// Gas cost increases in future hard forks might break this function. As an alternative, EIP-2930-type transactions using access lists can be employed. | ||
receive() external payable { | ||
emit NativeTokenDeposited(msg.sender, msg.value); | ||
} | ||
|
||
/// @notice Fallback to handle future versions of the [ERC-165](https://eips.ethereum.org/EIPS/eip-165) standard. | ||
/// @param _input An alias being equivalent to `msg.data`. This feature of the fallback function was introduced with the [solidity compiler version 0.7.6](https://github.com/ethereum/solidity/releases/tag/v0.7.6) | ||
/// @return The magic number registered for the function selector triggering the fallback. | ||
fallback(bytes calldata _input) external returns (bytes memory) { | ||
bytes4 magicNumber = _handleCallback(msg.sig, _input); | ||
return abi.encode(magicNumber); | ||
} | ||
|
||
/// @notice Emits the MetadataSet event if new metadata is set. | ||
/// @param _metadata Hash of the IPFS metadata object. | ||
function _setMetadata(bytes calldata _metadata) internal { | ||
emit MetadataSet(_metadata); | ||
} | ||
|
||
/// @notice Sets the trusted forwarder on the DAO and emits the associated event. | ||
/// @param _trustedForwarder The trusted forwarder address. | ||
function _setTrustedForwarder(address _trustedForwarder) internal { | ||
trustedForwarder = _trustedForwarder; | ||
|
||
emit TrustedForwarderSet(_trustedForwarder); | ||
} | ||
|
||
/// @notice Registers the ERC721/ERC1155 interfaces and callbacks. | ||
function _registerTokenInterfaces() private { | ||
_registerInterface(type(IERC721ReceiverUpgradeable).interfaceId); | ||
_registerInterface(type(IERC1155ReceiverUpgradeable).interfaceId); | ||
|
||
_registerCallback( | ||
IERC721ReceiverUpgradeable.onERC721Received.selector, | ||
IERC721ReceiverUpgradeable.onERC721Received.selector | ||
); | ||
_registerCallback( | ||
IERC1155ReceiverUpgradeable.onERC1155Received.selector, | ||
IERC1155ReceiverUpgradeable.onERC1155Received.selector | ||
); | ||
_registerCallback( | ||
IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector, | ||
IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector | ||
); | ||
} | ||
|
||
/// @inheritdoc IDAO_v1_0_0 | ||
function registerStandardCallback( | ||
bytes4 _interfaceId, | ||
bytes4 _callbackSelector, | ||
bytes4 _magicNumber | ||
) external override auth(REGISTER_STANDARD_CALLBACK_PERMISSION_ID) { | ||
_registerInterface(_interfaceId); | ||
_registerCallback(_callbackSelector, _magicNumber); | ||
emit StandardCallbackRegistered(_interfaceId, _callbackSelector, _magicNumber); | ||
} | ||
|
||
/// @inheritdoc IEIP4824 | ||
function daoURI() external view returns (string memory) { | ||
return _daoURI; | ||
} | ||
|
||
/// @notice Updates the set DAO uri to a new value. | ||
/// @param newDaoURI The new DAO uri to be set. | ||
function setDaoURI(string calldata newDaoURI) external auth(SET_METADATA_PERMISSION_ID) { | ||
_setDaoURI(newDaoURI); | ||
} | ||
|
||
/// @notice Sets the new DAO uri and emits the associated event. | ||
/// @param daoURI_ The new DAO uri. | ||
function _setDaoURI(string calldata daoURI_) internal { | ||
_daoURI = daoURI_; | ||
|
||
emit NewURI(daoURI_); | ||
} | ||
|
||
/// @notice This empty reserved space is put in place to allow future versions to add new variables without shifting down storage in the inheritance chain (see [OpenZepplins guide about storage gaps](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps)). | ||
uint256[47] private __gap; | ||
} |
Oops, something went wrong.