Skip to content

Commit

Permalink
new docs
Browse files Browse the repository at this point in the history
  • Loading branch information
novaknole committed Dec 3, 2024
1 parent 6989113 commit c086070
Show file tree
Hide file tree
Showing 11 changed files with 911 additions and 6 deletions.
136 changes: 136 additions & 0 deletions packages/contracts/docs/modules/ROOT/pages/core/actions.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
= Actions & Execution

== A Deep Dive into Actions and Execution

The DAO's `execute` function is part of the `DAO.sol` contract and has the following function header:

```solidity
function execute(
bytes32 _callId,
Action[] calldata _actions,
uint256 _allowFailureMap
)
external
override
auth(address(this), EXECUTE_PERMISSION_ID)
returns (bytes[] memory execResults, uint256 failureMap)
```

It offers two features that we will dive into in this article:

1. Execution of an array of arbitrary `Action` items.
2. Allowing failure of individual actions without reverting the entire transaction.

### Actions

In our framework, actions are represented by a solidity struct:

```solidity title="@aragon/osx/core/dao/IDAO.sol"
/// @notice The action struct to be consumed by the DAO's `execute` function resulting in an external call.
/// @param to The address to call.
/// @param value The native token value to be sent with the call.
/// @param data The bytes-encoded function selector and calldata for the call.
struct Action {
address to;
uint256 value;
bytes data;
}
```

Actions can be

- function calls to the DAO itself (e.g., to upgrade the DAO contract to a newer version of Aragon OSx)
- function calls to other contracts, such as

- external services (e.g. Uniswap, Compound, etc.)
- Aragon OSx plugins (e.g., the DAO can be a member of a multisig installed in another DAO),
- Aragon OSx protocol infrastructure (e.g., to xref:how-it-works/framework/plugin-management/plugin-setup/index.adoc[setup a plugin]).

- transfers of native tokens

#### Example: Calling the wETH Contract

We have an Aragon DAO deployed on the Goerli testnet. Now, we want to wrap `0.1 ETH` from the DAO treasury into `wETH` by depositing it into the link:https://goerli.etherscan.io/token/0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6#writeContract[Goerli WETH contract] deployed on the address `0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6`. The corresponding `Action` and `execute` function call look as follows:

```solidity

IDAO.Action[] memory actions = new IDAO.Action[](1);

actions[0] = IDAO.Action({
to: address(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6), // The address of the WETH contract on Goerli
value: 0.1 ether, // The Goerli ETH value to be sent with the function call
data: abi.encodeCall(IDAO.deposit, ()) // The calldata
});

dao().execute({_callId: '', _actions: actions, _allowFailureMap: 0});

```

For the `execute` call to work, the caller must have the required xref:how-it-works/core/permissions/index.adoc[`EXECUTE_PERMISSION_ID` permission] on the DAO contract.

### The Action Array

The `Action[] calldata _actions` input argument allows you to execute an array of up to 256 `Action` items in a single transaction within the gas limitations of the Ethereum virtual machine (EVM).
This is important so that several calls can be executed in a strict order within a single transaction.

Imagine a DAO is currently governed by a multisig (it has the `Multisig` plugin installed) and wants to transition the DAO governance to token voting.
To achieve this, one of the Multisig members creates a proposal in the plugin proposing to

1. install the `TokenVoting` plugin
2. uninstall the `Multisig` plugin

If enough multisig signers approve and the proposals passes, the action array can be executed and the transition happens.

Here, it is important that the two actions happen in the specified order and both are required to succeed.
Otherwise, if the first action would fail or the second one would happen beforehand, the DAO could end up in a state without having a governance plugin enabled.

Accordingly and by default, none of the atomic actions in the action array is allowed to revert. Otherwise, the entire action array is reverted.

### Allowing For Failure

In some scenarios, it makes sense to relax this requirement and allow specific actions in the array to revert.
Imagine a DAO wants to buy two NFTs A and B with DAI tokens. A proposal is scheduled in a governance plugin to

1. set the DAI allowance to `125`
2. try to buy NFT A for `100` DAI
3. try to buy NFT B for `125` DAI
4. set the DAI allowance to `0`

Once the proposal has passed, it might be that the NFT A or B was already bought by someone else so that action 2 or 3 would revert.
However, if the other NFT is still available, the DAO might still want to make at least the second trade.
Either way, the DAO needs to first approve `125` DAO tokens before the trade and wants to set it back to `0` after the potential trades have happened for security reasons.

#### The `allowFailureMap` Input Argument

This optionality can be achieved with the allow-failure feature available in the DAO's `execute` function.
To specify that failure is allowed for the actions 2 and 3, we provide the following `_allowFailureMap`:

```solidity
uint256 _allowFailureMap = 6;
```

and explain why in the following:

In binary representation a `uint256` value is a number with 256 digits that can be 0 or 1. In this representation, the value of `uint256(6)` translates into

```solidity
... 0 0 0 1 1 0 // the value 6 in binary representation
... 5 4 3 2 1 0 // the indices of the action array items that are allowed to fail.
```

where we omitted 250 more leading zeros. This binary value encodes a map to be read from right to left encoding the indices of the action array that are allowed to revert.
Accordingly, the second and third array item (with the indices 1 and 2) are allowed to fail.
If we want that every atomic succeeds, we specify a `allowFailureMap` value of `0`.

#### The `failureMap` Output Argument

In analogy and after an action array with a provided allow-failure map was executed successfully in the DAO, the `execute` function returns a corresponding `uint256 failureMap` containing the actual failures that have happened.
If all actions succeeded, the value `uint256(0)` will be returned.
If the third action failed, for example, because the NFT was already sold or the DAO had not enough DAI, a failure map value `uint256(4)` is returned

```solidity
... 0 0 0 1 0 0 // the value 4 in binary representation
... 5 4 3 2 1 0 // the indices of the action array items that are allowed to fail.
```

On the frontend, these conversions will be handled automatically.
53 changes: 53 additions & 0 deletions packages/contracts/docs/modules/ROOT/pages/core/dao.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
= DAO

== The DAO Contract: The Identity and Basis of Your Organization

In this section, you will learn about the core functionality of every Aragon OSx DAO.

The `DAO` contract is the identity and basis of your organization. It is the address carrying the DAO’s ENS name, metadata, and holding the funds. Furthermore, it has **six base functionalities** being commonly found in other DAO frameworks in the ecosystem.

### 1. Execution of Arbitrary Actions

The most important and basic functionality of your DAO is the **execution of arbitrary actions**, which allows you to execute the DAO's own functions as well as interacting with the rest of the world, i.e., calling methods in other contracts and sending assets to other addresses.

NOTE: Typically, actions are scheduled in a proposal in a governance - xref:how-it-works/core/plugins/index.adoc[plugin installed to your DAO]

Multiple `Action` structs can be put into one `Action[]` array and executed in a single transaction via the `execute` function. To learn more about actions and advanced features of the DAO executor, visit the xref:how-it-works/core/dao/actions.adoc[A Deep Dive Into Actions].

### 2. Asset Management

The DAO provides basic **asset management** functionality to deposit, withdraw, and keep track of

- native
- link:https://eips.ethereum.org/EIPS/eip-20[ERC-20 (Token Standard)]
- link:https://eips.ethereum.org/EIPS/eip-721[ERC-721 (NFT Standard)], and
- link:https://eips.ethereum.org/EIPS/eip-1155[ERC-1155 (Multi Token Standard)]

tokens in the treasury.
In the future, more advanced asset management and finance functionality can be added to your DAO in the form of xref:how-it-works/core/plugins/index.adoc[plugins].

### 3. Upgradeability

Your DAO contract has the ability to be upgraded to a newer version (see xref:how-to-guides/dao/protocol-upgrades.adoc[Upgrade your DAO]) if a new version of Aragon OSx is released in the future. These upgrades allow your DAO to smoothly transition to a new protocol version unlocking new features.


### 4. Callback Handling

To interact with the DAO, external contracts might require certain callback functions to be present.
Examples are the `onERC721Received` and `onERC1155Received` / `onERC1155BatchReceived` functions required by the link:https://eips.ethereum.org/EIPS/eip-721[ERC-721 (NFT Standard)] and link:https://eips.ethereum.org/EIPS/eip-1155[ERC-1155 (Multi Token Standard)] tokens.
Our `CallbackHandler` allows to register the required callback responses dynamically so that the DAO contract does not need to be upgraded.

### 5. Signature Validation

Currently, externally owned accounts (EOAs) can sign messages with their associated private keys, but contracts cannot.
An exemplary use case is a decentralized exchange with an off-chain order book, where buy/sell orders are signed messages.
To accept such a request, both, the external service provider and caller need to follow a standard with which the signed message of the caller can be validated.

By supporting the link:https://eips.ethereum.org/EIPS/eip-721[ERC-721 (NFT Standard)], your DAO can validate signatures via its `isValidSignature` function that forwards the call to a signature validator contract.

### 6. Permission Management

Lastly, it is essential that only the right entities (e.g., the DAO itself or trusted addresses) have permission to use the above-mentioned functionalities. This is why Aragon OSx DAOs contain a flexible and battle-tested **permission manager** being able to assign permissions for the above functionalities to specific addresses.
Although possible, the permissions to execute arbitrary actions or upgrade the DAO should not be given to EOAs as this poses a security risk to the organization if the account is compromised or acts adversarial. Instead, the permissions for the above-mentioned functionalities are better restricted to the `DAO` contract itself and triggered through governance xref:how-it-works/core/plugins/index.adoc[plugins] that you can install on your DAO.

To learn more, visit the xref:how-it-works/core/permissions/index.adoc[permission manager] section.
Loading

0 comments on commit c086070

Please sign in to comment.