Skip to content

Commit

Permalink
feat: added tests and preliminary versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
heueristik committed Apr 18, 2023
1 parent 48490d0 commit 8d856cc
Show file tree
Hide file tree
Showing 20 changed files with 682 additions and 47 deletions.
2 changes: 1 addition & 1 deletion packages/contracts/src/core/dao/DAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {hasBit, flipBit} from "../utils/BitMap.sol";
import {IEIP4824} from "./IEIP4824.sol";
import {IDAO} from "./IDAO.sol";

/// @title DAO
/// @title DAO v1.1.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.
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/src/core/dao/IDAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity 0.8.17;

/// @title IDAO
/// @title IDAO v1.1.0
/// @author Aragon Association - 2022-2023
/// @notice The interface required for DAOs within the Aragon App DAO framework.
interface IDAO {
Expand Down
340 changes: 340 additions & 0 deletions packages/contracts/src/core/dao/previous_versions/DAO_v1_0_0.sol
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;
}
Loading

0 comments on commit 8d856cc

Please sign in to comment.