From 5a65a5f9a3154d2685ad1694ffe5211302ce5696 Mon Sep 17 00:00:00 2001 From: Giorgi Lagidze Date: Sun, 17 Nov 2024 15:32:45 +0400 Subject: [PATCH] changes --- .../pages/how-it-works/framework-dao.adoc | 6 +- .../how-it-works/framework/ens-names.adoc | 13 ++-- .../how-to-guides/dao/action-execution.adoc | 26 +++---- .../how-to-guides/dao/best-practices.adoc | 6 +- .../ROOT/pages/how-to-guides/dao/index.adoc | 20 ++---- .../how-to-guides/dao/managing-plugins.adoc | 48 ++++++------- .../how-to-guides/dao/protocol-upgrades.adoc | 8 +-- .../ROOT/pages/how-to-guides/index.adoc | 14 ++-- .../plugin-development/best-practices.adoc | 15 ++-- .../governance-plugins/index.adoc | 26 +++---- .../governance-plugins/membership.adoc | 22 +++--- .../governance-plugins/proposals.adoc | 6 +- .../plugin-development/index.adoc | 41 +++++------ .../plugin-development/meta-tx-plugins.adoc | 6 +- .../implementation.adoc | 42 ++++++----- .../non-upgradeable-plugin/index.adoc | 22 +++--- .../initialization.adoc | 43 ++++++----- .../non-upgradeable-plugin/setup.adoc | 13 ++-- .../plugin-development/plugin-types.adoc | 42 +++++------ .../plugin-development/publication/index.adoc | 26 ++++--- .../publication/metadata.adoc | 72 +++++++++++++------ .../publication/versioning.adoc | 23 +++--- .../upgradeable-plugin/implementation.adoc | 10 ++- .../upgradeable-plugin/index.adoc | 28 ++++---- .../upgradeable-plugin/initialization.adoc | 44 +++++++----- .../upgradeable-plugin/setup.adoc | 44 +++++++----- .../upgradeable-plugin/subsequent-builds.adoc | 58 +++++++++------ .../upgradeable-plugin/updating-versions.adoc | 45 +++++++----- 28 files changed, 403 insertions(+), 366 deletions(-) diff --git a/components/osx/modules/ROOT/pages/how-it-works/framework-dao.adoc b/components/osx/modules/ROOT/pages/how-it-works/framework-dao.adoc index bf7ee42f..c2c9c5e7 100644 --- a/components/osx/modules/ROOT/pages/how-it-works/framework-dao.adoc +++ b/components/osx/modules/ROOT/pages/how-it-works/framework-dao.adoc @@ -1,8 +1,6 @@ ---- -title: Protocol Governance ---- += Protocol Governance -## Governing the Aragon OSx Framework +=== Governing the Aragon OSx Framework To govern the framework infrastructure, a **Framework DAO** was deployed. diff --git a/components/osx/modules/ROOT/pages/how-it-works/framework/ens-names.adoc b/components/osx/modules/ROOT/pages/how-it-works/framework/ens-names.adoc index 36446e45..8789d4a1 100644 --- a/components/osx/modules/ROOT/pages/how-it-works/framework/ens-names.adoc +++ b/components/osx/modules/ROOT/pages/how-it-works/framework/ens-names.adoc @@ -1,14 +1,11 @@ ---- -title: ENS Names ---- += ENS Names -## Unique DAO and Plugin Repo Names +=== Unique DAO and Plugin Repo Names -To make DAOs and plugin repositories easily identifiable in the Aragon OSx ecosystem, we assign unique ENS names to them upon registration during the [DAO creation](./01-dao-creation/index.md) and [plugin publishing](./02-plugin-management/01-plugin-repo/01-plugin-repo-creation.md) processes. +To make DAOs and plugin repositories easily identifiable in the Aragon OSx ecosystem, we assign unique ENS names to them upon +registration during the xref:how-it-works/framework/dao-creation/index.adoc[DAO creation] and xref:how-it-works/framework/plugin-management/plugin-repo/plugin-repo-creation.md[plugin publishing] processes. -:::info -You can skip registering an ENS name for your DAO under the `dao.eth` by leaving the [`DAOSettings.subdomain` field](../../03-reference-guide/framework/dao/DAOFactory.md#public-struct-daosettings) empty when calling the [`createDao`](../../03-reference-guide/framework/dao/DAOFactory.md#external-function-createdao) function. -::: +TIP: You can skip registering an ENS name for your DAO under the `dao.eth` by leaving the TODO:GIORGI [`DAOSettings.subdomain` field](../../03-reference-guide/framework/dao/DAOFactory.md#public-struct-daosettings) empty when calling the [`createDao`](../../03-reference-guide/framework/dao/DAOFactory.md#external-function-createdao) function. ### Allowed Character Set diff --git a/components/osx/modules/ROOT/pages/how-to-guides/dao/action-execution.adoc b/components/osx/modules/ROOT/pages/how-to-guides/dao/action-execution.adoc index 4344ae90..b90092e9 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/dao/action-execution.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/dao/action-execution.adoc @@ -1,33 +1,29 @@ ---- -title: Executing actions on behalf of the DAO ---- += Executing actions on behalf of the DAO ## Using the DAO Executor -Executing actions on behalf of the DAO is done through the `execute` function from the `DAO.sol` contract. This function allows us to [pass an array of actions)](https://github.com/aragon/osx/blob/develop/packages/contracts/src/core/dao/DAO.sol) to be executed by the DAO contract itself. +Executing actions on behalf of the DAO is done through the `execute` function from the `DAO.sol` contract. This function allows us to link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/core/dao/DAO.sol[pass an array of actions] to be executed by the DAO contract itself. -However, for the `execute` call to work, the address calling the function (the `msg.sender`) needs to have the [`EXECUTE_PERMISSION`](../../01-how-it-works/01-core/02-permissions/index.md#permissions-native-to-the-dao-contract). This is to prevent anyone from being able to execute actions on behalf of the DAO and keep your assets safe from malicious actors. +However, for the `execute` call to work, the address calling the function (the `msg.sender`) needs to have the xref:how-it-works/core/permissions/index.adoc#permissions-native-to-the-dao-contract[`EXECUTE_PERMISSION`]. This is to prevent anyone from being able to execute actions on behalf of the DAO and keep your assets safe from malicious actors. ## How to grant the Execute Permission Usually, the `EXECUTE_PERMISSION` is granted to a governance plugin of the DAO so that only the approved proposals can be executed. For example, we'd grant the `EXECUTE_PERMISSION` to the address of the Multisig Plugin. That way, when a proposal is approved by the required members of the multisig, the plugin is able to call the `execute` function on the DAO in order to get the actions executed. -To grant the `EXECUTE_PERMISSION` to an address, you'll want to call on the `PermissionManager`'s [`grant` function](https://github.com/aragon/osx/blob/develop/packages/contracts/src/core/permission/PermissionManager.sol#L105) and pass it 4 parameters: +To grant the `EXECUTE_PERMISSION` to an address, you'll want to call on the `PermissionManager`'s link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/core/permission/PermissionManager.sol#L105[grant function] and pass it 4 parameters: - `where`: the address of the contract containing the function `who` wants access to - `who`: the address (EOA or contract) receiving the permission - `permissionId`: the permission identifier the caller needs to have in order to be able to execute the action - `condition`: the address of the condition contract that will be asked (if any) before authorizing the call to happen -:::caution -You probably don't want to grant `EXECUTE_PERMISSION` to any random address, since this gives the address access to execute any action on behalf of the DAO. We recommend you only grant `EXECUTE_PERMISSION` to governance plugins to ensure the safety of your assets. Granting `EXECUTE_PERMISSION` to an externally owned account is considered an anti-pattern. -::: +CAUTION: You probably don't want to grant `EXECUTE_PERMISSION` to any random address, since this gives the address access to execute any action on behalf of the DAO. We recommend you only grant `EXECUTE_PERMISSION` to governance plugins to ensure the safety of your assets. Granting `EXECUTE_PERMISSION` to an externally owned account is considered an anti-pattern. ## Examples ### Calling a DAO Function -Imagine you want to call an internal function inside the `DAO` contract, for example, to manually [grant or revoke a permission](../../01-how-it-works/01-core/02-permissions/index.md). The corresponding `Action` and `execute` function call look as follows: +Imagine you want to call an internal function inside the `DAO` contract, for example, to manually xref:how-it-works/core/permissions/index.adoc[grant or revoke a permission]. The corresponding `Action` and `execute` function call look as follows: ```solidity function exampleGrantCall( @@ -54,13 +50,11 @@ function exampleGrantCall( } ``` -Here we use the selector of the [`grant` function](../../03-reference-guide/core/permission/PermissionManager.md#external-function-grant). To revoke the permission, the selector of the [`revoke` function](../../03-reference-guide/core/permission/PermissionManager.md#external-function-revoke) must be used. +Here we use the selector of the TODO:GIORGI [`grant` function](../../03-reference-guide/core/permission/PermissionManager.md#external-function-grant). To revoke the permission, the selector of the [`revoke` function](../../03-reference-guide/core/permission/PermissionManager.md#external-function-revoke) must be used. -If the caller possesses the [`ROOT_PERMISSION_ID` permission](../../01-how-it-works/01-core/02-permissions/index.md#permissions-native-to-the-dao-contract) on the DAO contract, the call becomes simpler and cheaper: +If the caller possesses the xref:how-it-works/core/permissions/index.adoc#permissions-native-to-the-dao-contract[`ROOT_PERMISSION_ID` permission] on the DAO contract, the call becomes simpler and cheaper: -:::caution -Granting the `ROOT_PERMISSION_ID` permission to other contracts other than the `DAO` contract is dangerous and considered as an anti-pattern. -::: +CAUTION: Granting the `ROOT_PERMISSION_ID` permission to other contracts other than the `DAO` contract is dangerous and considered as an anti-pattern. ```solidity function exampleGrantFunction(DAO dao, address _where, address _who, bytes32 _permissionId) { @@ -123,7 +117,7 @@ function exampleFunctionCall( ### Calling a Payable Function -Wrap `0.1 ETH` from the DAO treasury into `wETH` by depositing it into the [Goerli WETH9 contract](https://goerli.etherscan.io/token/0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6#writeContract). +Wrap `0.1 ETH` from the DAO treasury into `wETH` by depositing it into the link:https://goerli.etherscan.io/token/0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6#writeContract[Goerli WETH9 contract]. The corresponding `Action` and `execute` function call look as follows: ```solidity diff --git a/components/osx/modules/ROOT/pages/how-to-guides/dao/best-practices.adoc b/components/osx/modules/ROOT/pages/how-to-guides/dao/best-practices.adoc index 987d064b..e121fa42 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/dao/best-practices.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/dao/best-practices.adoc @@ -1,8 +1,6 @@ ---- -title: Best Practices ---- += Best Practices -## Some Advice When Operating your DAO +=== Some Advice When Operating your DAO ### DOs 👌 diff --git a/components/osx/modules/ROOT/pages/how-to-guides/dao/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/dao/index.adoc index 266deb41..bd10b753 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/dao/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/dao/index.adoc @@ -1,7 +1,4 @@ ---- -title: How to Operate a DAO -sidebar_label: Operating a DAO ---- += How to Operate a DAO DAOs are decentralized autonomous organizations. They are a group of people managing on-chain assets through a set of smart contracts. @@ -11,18 +8,13 @@ DAOs manage assets through collective decision-making mechanisms. Although a lot In Aragon OSx, DAOs are a treasury and a permission management system - all other functionality is enabled through "capsules of opt-in functionality allowing the DAO to work in custom ways". These are called Plugins. -:::note -Plugins are smart contracts which extend the functionality of what the DAO can do. They are able to execute actions on behalf of the DAO through permissions the DAO grants or revokes them. -::: +NOTE: Plugins are smart contracts which extend the functionality of what the DAO can do. They are able to execute actions on behalf of the DAO through permissions the DAO grants or revokes them. Decision-making mechanisms are one example of Plugins. Treasury management, action bundles, or connections to other smart contracts are others. - - - In this section, we'll go through how to operate and maintain your DAO: -- [A Summary of Best Practices](./01-best-practices.md) -- [How to execute actions on behalf of the DAO](./02-action-execution.md) -- [How to manage a DAO's plugins](./04-managing-plugins/index.md) -- [How to upgrade your DAO to a future Aragon OSx Releases](./03-protocol-upgrades.md) +- xref:how-to-guides/dao/best-practices.adoc[A Summary of Best Practices] +- xref:how-to-guides/dao/action-execution.adoc[How to execute actions on behalf of the DAO] +- xref:how-to-guides/dao/managing-plugins.adoc[How to manage a DAO's plugins] +- xref:how-to-guides/dao/protocol-upgrades.adoc[How to upgrade your DAO to a future Aragon OSx Releases] diff --git a/components/osx/modules/ROOT/pages/how-to-guides/dao/managing-plugins.adoc b/components/osx/modules/ROOT/pages/how-to-guides/dao/managing-plugins.adoc index 57f31704..d8167ded 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/dao/managing-plugins.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/dao/managing-plugins.adoc @@ -1,50 +1,48 @@ ---- -title: Manage your DAO's Plugins ---- += Manage your DAO's Plugins -## How to manage the Plugins within your DAO +=== How to manage the Plugins within your DAO - +You can install, uninstall or update any plugin into your DAO. If you want to dive deeper into plugins, check out xref:how-it-works/core/plugins/index.adoc[how plugins work here]. -You can install, uninstall or update any plugin into your DAO. If you want to dive deeper into plugins, check out [how plugins work here](../../../01-how-it-works/01-core/03-plugins/index.md). - -Before diving deeper into this guide, make sure that you understand [permissions](../../../01-how-it-works/01-core/02-permissions/index.md) and know about the [DAO executor](../../../01-how-it-works/01-core/01-dao/index.md). +Before diving deeper into this guide, make sure that you understand xref:how-it-works/core/permissions/index.adoc[permissions]and know about the xref:how-it-works/core/dao/index.adoc[DAO executor]. #### How to create a DAO with Plugins When you create your DAO, you must **install at least one functioning governance plugin** (meaning one plugin having the `EXECUTION_PERMISSION`) so your have a mechanism of executing actions on behalf of your DAO. This is crucial because otherwise nobody can operate the DAO and it would become incapacitated right after it was created. You would have spent gas for nothing. -:::info -If you create your DAO through the [Aragon App](https://app.aragon.org) or the [Aragon SDK](https://devs.aragon.org/docs/sdk), this will be checked and you will be warned in case you have not selected a suitable Aragon plugin. -::: - -Although the easiest (and recommended) way to create your DAO is through the [Aragon App](https://app.aragon.org) or the [Aragon SDK](https://devs.aragon.org/docs/sdk), you can also do it directly from the protocol through calling on the [`createDAO` function](https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/dao/DAOFactory.sol#L63) from the `DAOFactory` contract and passing it the calldata `DAOSettings` for your DAO as well as the `PluginSettings` array referencing the plugins and the settings to be installed upon DAO creation. +NOTE: If you create your DAO through the link:https://app.aragon.org[Aragon App] or the link:https://devs.aragon.org/docs/sdk[Aragon SDK], this will be checked and you will be warned in case you have not selected a suitable Aragon plugin. - +Although the easiest (and recommended) way to create your DAO is through the link:https://app.aragon.org[Aragon App] or +the link:https://devs.aragon.org/docs/sdk[Aragon SDK], you can also do it directly from the protocol through calling +on the link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/dao/DAOFactory.sol#L63[`createDAO` function] +from the `DAOFactory` contract and passing it the calldata `DAOSettings` for your DAO as well as the `PluginSettings` array +referencing the plugins and the settings to be installed upon DAO creation. #### How to change a DAO's Governance Setup after a DAO has been created -After a DAO is created with at least one plugin installed with `EXECUTE_PERMISSION` on the DAO, it's likely you may want to change change your governance setup later on by [installing, updating, or uninstalling plugins](../../../01-how-it-works/02-framework/02-plugin-management/02-plugin-setup/index.md). +After a DAO is created with at least one plugin installed with `EXECUTE_PERMISSION` on the DAO, it's likely you may want to change change your governance setup later +on by xref:how-it-works/framework/plugin-management/plugin-setup/index.adoc[installing, updating, or uninstalling plugins]. -Here, it is very important that you **maintain at least one functioning governance plugin** (a contract with `EXECUTE_PERMISSION` on the DAO) so that your assets are not locked in the future. In that regard, you want to be careful to not accidentally: +Here, it is very important that you **maintain at least one functioning governance plugin** (a contract with `EXECUTE_PERMISSION` on the DAO) so that your +assets are not locked in the future. In that regard, you want to be careful to not accidentally: - uninstall every plugin within your DAO, or - update or upgrade the plugin or otherwise change the internal plugin settings. -If you do that, nobody would be able to create proposals and execute actions on the DAO anymore. Accordingly, DAOs must review proposals requesting to change the governance setup with utmost care before voting for them. In the next section, we explain how to review a proposal properly and what to pay attention too. - - +If you do that, nobody would be able to create proposals and execute actions on the DAO anymore. Accordingly, DAOs must review +proposals requesting to change the governance setup with utmost care before voting for them. In the next section, +we explain how to review a proposal properly and what to pay attention too. ### How to maintain Execution Permission on the DAO A very important thing to consider when operating your DAO is to make sure that you do not lock it - meaning, you allow it into a state where the DAO cannot execute actions anymore. -The accidental loss of the permission to execute actions on your DAO ([the `EXECUTION_PERMISSION_ID` permission](../../../01-how-it-works/01-core/02-permissions/index.md#permissions-native-to-the-dao-contract)) incapacitates your DAO. If this happens, you are not able to withdraw funds or act through the DAO, unless you have the `ROOT_PERMISSION_ID` on the DAO. +The accidental loss of the permission to execute actions on your DAO (xref:how-it-works/core/permissions/index.adoc#permissions-native-to-the-dao-contract[the `EXECUTION_PERMISSION_ID` permission]) incapacitates your DAO. +If this happens, you are not able to withdraw funds or act through the DAO, unless you have the `ROOT_PERMISSION_ID` on the DAO. -:::danger -Do not interact directly with the smart contracts unless you know exactly what you are doing, **especially if this involves granting or revoking permissions**. Instead, use the Aragon App or Aragon SDK for creating and managing your DAO and interacting with the smart contracts. -::: +IMPORTANT: Do not interact directly with the smart contracts unless you know exactly what you are doing, **especially if this involves granting or revoking permissions**. +Instead, use the Aragon App or Aragon SDK for creating and managing your DAO and interacting with the smart contracts. -If you interact with the Aragon OSx protocol through the Aragon App frontend or the Aragon SDK and use only audited and verified plugins, this will not happen. -However, diligence and care is required in some situations. +If you interact with the Aragon OSx protocol through the Aragon App frontend or the Aragon SDK and use only audited and verified plugins, +this will not happen. However, diligence and care is required in some situations. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/dao/protocol-upgrades.adoc b/components/osx/modules/ROOT/pages/how-to-guides/dao/protocol-upgrades.adoc index ad874d1b..b320415c 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/dao/protocol-upgrades.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/dao/protocol-upgrades.adoc @@ -1,8 +1,6 @@ ---- -title: Upgrade your DAO to future Aragon OSx Versions ---- += Upgrade your DAO to future Aragon OSx Versions -## Upgrading to Future Aragon OSx Protocol Versions +=== Upgrading to Future Aragon OSx Protocol Versions At Aragon, we are constantly working on improving the Aragon OSx framework. @@ -10,4 +8,4 @@ To make it easy for your DAO to switch to future Aragon OSx protocol versions, w Whenever we provide a new Aragon OSx protocol version, it will be possible to upgrade your DAO and associated plugins through your DAO dashboard. -[Add your email here](https://aragondevelopers.substack.com/) if you'd like to receive updates when this happens! +link:https://aragondevelopers.substack.com/[Add your email here] if you'd like to receive updates when this happens! diff --git a/components/osx/modules/ROOT/pages/how-to-guides/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/index.adoc index 9b8e9bf6..3931331d 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/index.adoc @@ -1,9 +1,6 @@ ---- -title: Tutorials -sidebar_label: Tutorials ---- += Tutorials -## Welcome to our Tutorials on Using the Aragon OSx Protocol! +=== Welcome to our Tutorials on Using the Aragon OSx Protocol! With a few lines of code, the Aragon OSx protocol allows you create, manage, and change your on-chain organizations, through extending functionality for DAOs through the installation and uninstallation of plugins. @@ -36,7 +33,8 @@ On the technical level, plugins are composed of two key contracts: - ⚡️ The Plugin implementation contract: containing all of the logic and functionality for your DAO, and - 👩🏻‍🏫 The Plugin Setup contract: containing the installation, uninstallation and upgrade instructions for your plugin. -![Aragon OSx Plugins](/img/plugins/what_is_a_plugin.png) +image::../../../../../_/images/img/plugins/what_is_a_plugin.png[align="center"] + Through plugins, we provide a secure, flexible way for on-chain organizations to iterate as they grow. @@ -44,5 +42,5 @@ We enable everyone to experiment with governance at the speed of software! Check out our How-To-Guides on: -- [How to Develop your own Plugin](./02-plugin-development/index.md) -- [How to Operate your DAO](./01-dao/index.md) +- xref:how-to-guides/plugin-development/index.adoc[How to Develop your own Plugin] +- xref:how-to-guides/dao/index.adoc[How to Operate your DAO] diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/best-practices.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/best-practices.adoc index 7230dc4e..9813d7d1 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/best-practices.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/best-practices.adoc @@ -1,18 +1,16 @@ ---- -title: Before Starting ---- += Before Starting -## Advice for Developing a Plugin +=== Advice for Developing a Plugin ### DOs 👌 -- Document your contracts using [NatSpec](https://docs.soliditylang.org/en/v0.8.17/natspec-format.html). -- Test your contracts, e.g., using toolkits such as [hardhat (JS)](https://hardhat.org/hardhat-runner/docs/guides/test-contracts) or [Foundry (Rust)](https://book.getfoundry.sh/forge/tests). +- Document your contracts using link:https://docs.soliditylang.org/en/v0.8.17/natspec-format.html[NatSpec]. +- Test your contracts, e.g., using toolkits such as link:https://hardhat.org/hardhat-runner/docs/guides/test-contracts[hardhat (JS)] or link:https://book.getfoundry.sh/forge/tests[Foundry (Rust)]. - Use the `auth` modifier to control the access to functions in your plugin instead of `onlyOwner` or similar. - Write plugins implementations that need minimal permissions on the DAO. - Write `PluginSetup` contracts that remove all permissions on uninstallation that they requested during installation or updates. - Plan the lifecycle of your plugin (need for upgrades). -- Follow our [versioning guidelines](../02-plugin-development/07-publication/01-versioning.md). +- Follow our xref:how-to-guides/plugin-development/publication/versioning.adoc[versioning guidelines]. ### DON'Ts ✋ @@ -24,7 +22,4 @@ title: Before Starting - Repurpose existing storage (in upgradeable plugins). - Inherit from previous versions as this can mess up the inheritance chain. Instead, write self-contained contracts. - - - In the following sections, you will learn about the details about plugin development. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/index.adoc index 1cefa457..2f47967d 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/index.adoc @@ -1,14 +1,14 @@ ---- -title: Governance Plugins ---- += Governance Plugins -## How to Build a Governance Plugin +== How to Build a Governance Plugin -One of the most common use cases for plugins are governance plugins. Governance plugins are plugins DAOs install to help them make decisions. +One of the most common use cases for plugins are governance plugins. Governance plugins are plugins DAOs install to help +them make decisions. ### What are Governance Plugins -Governance plugins are characterized by the **ability to execute actions in the DAO** they have been installed to. Accordingly, the `EXECUTE_PERMISSION_ID` is granted on installation on the installing DAO to the governance plugin contract. +Governance plugins are characterized by the **ability to execute actions in the DAO** they have been installed to. +Accordingly, the `EXECUTE_PERMISSION_ID` is granted on installation on the installing DAO to the governance plugin contract. ```solidity grant({ @@ -20,18 +20,14 @@ grant({ Beyond this fundamental ability, governance plugins usually implement two interfaces: -- [The `IProposal` interface](./01-proposals.md) introducing the **notion of proposals** and how they are created and executed. -- [The `IMembership` interface](./02-membership.md) introducing the **notion of membership** to the DAO. +- xref:how-to-guides/plugin-development/governance-plugins/proposals.adoc[The IProposal interface] introducing the **notion of proposals** and how they are created and executed. +- xref:how-to-guides/plugin-development/governance-plugins/proposals.adoc[The IMembership interface] introducing the **notion of membership** to the DAO. ### Examples of Governance Plugins Some examples of governance plugins are: -- [A token-voting plugin](https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/majority-voting/token): Results are based on what the majority votes and the vote's weight is determined by how many tokens an account holds. Ex: Alice has 10 tokens, Bob 2, and Alice votes yes, the yes wins. -- [Multisig plugin](https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/multisig): A determined set of addresses is able to approve. Once `x` amount of addresses approve (as determined by the plugin settings), then the proposal automatically succeeds. -- [Admin plugin](https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/admin): One address can create and immediately execute proposals on the DAO (full control). -- [Addresslist plugin](https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/majority-voting/addresslist): Majority-based voting, where list of addresses are able to vote in decision-making for the organization. Unlike a multisig, everybody here is expected to vote yes/no/abstain within a certain time frame. +- link:https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/majority-voting/token[A token-voting plugin]: Results are based on what the majority votes and the vote's weight is determined by how many tokens an account holds. Ex: Alice has 10 tokens, Bob 2, and Alice votes yes, the yes wins. +- link:https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/multisig[Multisig plugin]: A determined set of addresses is able to approve. Once `x` amount of addresses approve (as determined by the plugin settings), then the proposal automatically succeeds. +- link:https://github.com/aragon/osx/tree/main/packages/contracts/src/plugins/governance/admin[Admin plugin]: One address can create and immediately execute proposals on the DAO (full control). - - - diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/membership.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/membership.adoc index 3f0a8cb7..99c9288f 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/membership.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/membership.adoc @@ -1,10 +1,10 @@ ---- -title: Membership ---- += Membership -## The `IMembership` Interface +== The `IMembership` Interface -The `IMembership` interface defines common functions and events for for plugins that keep track of membership in a DAO. This plugins can be used to define who can vote on proposals, who can create proposals, etc. The list of members can be defined in the plugin itself or by a contract that defines the membership like an ERC20 or ERC721 token. +The `IMembership` interface defines common functions and events for for plugins that keep track of membership in a DAO. +This plugins can be used to define who can vote on proposals, who can create proposals, etc. The list of members can be defined +in the plugin itself or by a contract that defines the membership like an ERC20 or ERC721 token. The interface is defined as follows: @@ -35,21 +35,25 @@ The interface contains three events and one function. ### `MembersAdded` event -The members added event should be emitted when members are added to the DAO plugin. It only contains one `address[] members` parameter that references the list of new members being added. +The members added event should be emitted when members are added to the DAO plugin. It only contains one +`address[] members` parameter that references the list of new members being added. - `members`: The list of new members being added. ### `MembersRemoved` event -The members added event should be emitted when members are removed from the DAO plugin. It only contains one `address[] members` parameter that references the list of members being removed. +The members added event should be emitted when members are removed from the DAO plugin. It only contains one `address[] members` +parameter that references the list of members being removed. ### `MembershipContractAnnounced` event -This event should be emitted during the initialization of the membership plugin to announce the membership being defined by a contract. It contains the defining contract as a parameter. +This event should be emitted during the initialization of the membership plugin to announce the membership being defined by a contract. +It contains the defining contract as a parameter. ### `isMember` function -This is a simple function that should be implemented in the plugin contract that introduces the members to the DAO. It checks if an account is a member of the DAO and returns a boolean value. +This is a simple function that should be implemented in the plugin contract that introduces the members to the DAO. It checks if an +account is a member of the DAO and returns a boolean value. ## Usage diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/proposals.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/proposals.adoc index 08a48787..8953aba0 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/proposals.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/governance-plugins/proposals.adoc @@ -1,8 +1,6 @@ ---- -title: Proposals ---- += Proposals -## The `IProposal` Interface +== The `IProposal` Interface The `IProposal` interface is used to create and execute proposals containing actions and a description. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/index.adoc index c2ff7d4f..7a922757 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/index.adoc @@ -1,9 +1,6 @@ ---- -title: How to build a DAO Plugin -sidebar_label: Developing a Plugin ---- += How to build a DAO Plugin -## Plugin Development Quickstart Guide +=== Plugin Development Quickstart Guide Plugins are how we extend the functionality for DAOs. In Aragon OSx, everything a DAO can do is based on Plugin functionality enabled through permissions. @@ -11,15 +8,18 @@ In this Quickstart guide, we will use the Aragon Hardhat template to set up a pl ## Setting up your environment -We recommend using our [hardhat template](https://github.com/aragon/osx-plugin-template-hardhat) to get started. If you don't have it installed, you can do so by running: +We recommend using our link:https://github.com/aragon/osx-plugin-template-hardhat[hardhat template] to get started. If you don't have +it installed, you can do so by running: ```bash git clone github.com/aragon/osx-plugin-template-hardhat ``` -Once you have cloned the repository the first step is to add a `.env` file with your `ALCHEMY_API_KEY`, there is a [`.env.example`](https://github.com/aragon/osx-plugin-template-hardhat/blob/main/.env.example) file you can use as a template. +Once you have cloned the repository the first step is to add a `.env` file with your `ALCHEMY_API_KEY`, +there is a link:https://github.com/aragon/osx-plugin-template-hardhat/blob/main/.env.example[.env.example] file you can use as a template. -This file contains more env variables that you may need throughout the development process, but to get started you only need to add the `ALCHEMY_API_KEY`. +This file contains more env variables that you may need throughout the development process, but to get started you only need to +add the `ALCHEMY_API_KEY`. ```bash # INCOMPLETE - PLEASE FILL IN THE MISSING VALUES @@ -40,26 +40,27 @@ ALCHEMY_API_KEY="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" Once the `.env` file is created, you can run the following command to install the dependencies: ```bash - yarn install && cd packages/contracts && yarn install && yarn build && yarn typechain ``` -Now you are ready to start developing your plugin. You should have two files called `MyPlugin.sol` and `MyPluginSetup.sol` inside the `contracts` folder. +Now you are ready to start developing your plugin. You should have two files called `MyPlugin.sol` and `MyPluginSetup.sol` inside +the `contracts` folder. -The template is already set up with a basic plugin and plugin setup contract. You can start by modifying these files to create your own plugin. The tests and deployment scripts are also set up for you to use. +The template is already set up with a basic plugin and plugin setup contract. You can start by modifying these files to create +your own plugin. The tests and deployment scripts are also set up for you to use. ## Next Steps -For more information on how to use the template, you can check the [README](https://github.com/aragon/osx-plugin-template-hardhat/blob/main/README.md) and the [USAGE GUIDE](https://github.com/aragon/osx-plugin-template-hardhat/blob/main/USAGE_GUIDE.md) +For more information on how to use the template, you can check the link:https://github.com/aragon/osx-plugin-template-hardhat/blob/main/README.md[README] and +the link:https://github.com/aragon/osx-plugin-template-hardhat/blob/main/USAGE_GUIDE.md[USAGE GUIDE]. For more information on how to develop a plugin, you can our plugin development guides: -- [Best practices and patterns](./01-best-practices.md) -- [Different plugin types](./02-plugin-types.md) -- [Non-upgradeable plugin](./03-non-upgradeable-plugin/index.md) -- [Upgradeable plugin](./04-upgradeable-plugin/index.md) -- [Governance plugin](./05-governance-plugins/index.md) +- xref:how-to-guides/plugin-development/best-practices.adoc[Best practices and patterns] +- xref:how-to-guides/plugin-development/plugin-types.adoc[Different plugin types] +- xref:how-to-guides/plugin-development/non-upgradeable-plugin.adoc[Non-upgradeable plugin] +- xref:how-to-guides/plugin-development/upgradeable-plugin.adoc[Upgradeable plugin] +- xref:how-to-guides/plugin-development/governance-plugins/index.adoc[Governance plugin] -:::danger -This plugin template uses version `1.4.0-alpha.5` of the Aragon OSx protocol. This version is still in development and is not audited yet. -::: +IMPORTANT: This plugin template uses version TODO:GIORGI `1.4.0-alpha.5` of the Aragon OSx protocol. This version is still in development and +is not audited yet. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/meta-tx-plugins.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/meta-tx-plugins.adoc index a195e393..3327bfe5 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/meta-tx-plugins.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/meta-tx-plugins.adoc @@ -1,10 +1,8 @@ ---- -title: Meta Transactions ---- += Meta Transactions ## Support for Meta Transactions -Our plugins are compatible with the [ERC-2771 (Meta Transaction)](https://eips.ethereum.org/EIPS/eip-2771) standard, which allows users to send gasless transactions, also known as meta transactions. +Our plugins are compatible with the link:https://eips.ethereum.org/EIPS/eip-2771[ERC-2771 (Meta Transaction)] standard, which allows users to send gasless transactions, also known as meta transactions. This is possible because we use `_msgSender` and `_msgData` context from OpenZeppelin's `Context` and `ContextUpgradeable` in our `Plugin`, `PluginCloneable`, and `PluginUUPSUpgradeable` classes. To support meta transactions, your implementation contract must inherit and override the `Context` implementation with the `_msgSender` and `_msgData` functions provided in OpenGSN's `BaseRelayRecipient`, and use the DAO's trusted forwarder. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc index f8444bd1..236672f4 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc @@ -1,14 +1,15 @@ ---- -title: Plugin Implementation Contract ---- += Plugin Implementation Contract -## How to Build a Non-Upgradeable Plugin +=== How to Build a Non-Upgradeable Plugin -Once we've initialized our plugin (take a look at our guide on [how to initialize Non-Upgradeable Plugins here](./01-initialization.md)), we can start using the Non-Upgradeable Base Template to perform actions on the DAO. +Once we've initialized our plugin (take a look at our guide on xref:how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc[how to initialize Non-Upgradeable Plugins here]), +we can start using the Non-Upgradeable Base Template to perform actions on the DAO. ### 1. Set the Permission Identifier -Firstly, we want to define a [permission identifier](../../../01-how-it-works/01-core/02-permissions/index.md#permission-identifiers) `bytes32` constant at the top of the contract and establish a `keccak256` hash of the permission name we want to choose. In this example, we're calling it the `ADMIN_EXECUTE_PERMISSION`. +Firstly, we want to define a xref:how-it-works/core/permissions/index.adoc#permission-identifiers[permission identifier] `bytes32` constant at the top +of the contract and establish a `keccak256` hash of the permission name we want to choose. +In this example, we're calling it the `ADMIN_EXECUTE_PERMISSION`. ```solidity contract SimpleAdmin is PluginCloneable { @@ -32,9 +33,9 @@ contract SimpleAdmin is PluginCloneable { } ``` -:::note -You are free to choose the permission name however you like. For example, you could also have used `keccak256('SIMPLE_ADMIN_PLUGIN:PERMISSION_1')`. However, it is important that the permission names are descriptive and cannot be confused with each other. -::: +NOTE: +You are free to choose the permission name however you like. For example, you could also have used `keccak256('SIMPLE_ADMIN_PLUGIN:PERMISSION_1')`. +However, it is important that the permission names are descriptive and cannot be confused with each other. Setting this permission is key because it ensures only signers who have been granted that permission are able to execute functions. @@ -42,9 +43,13 @@ Setting this permission is key because it ensures only signers who have been gra Now that we have created the permission, we will use it to protect the implementation. We want to make sure only the authorized callers holding the `ADMIN_EXECUTE_PERMISSION`, can use the function. -Because we have initialized the [`PluginCloneable` base contract](https://github.com/aragon/osx-commons/blob/develop/contracts/src/plugin/PluginCloneable.sol), we can now use its features, i.e., the [`auth` modifier](https://github.com/aragon/osx-commons/blob/1cf46ff15dbda8481f9ee37558e7ea8b257d51f2/contracts/src/permission/auth/DaoAuthorizable.sol#L30-L35) provided through the `DaoAuthorizable` base class. The `auth('ADMIN_EXECUTE_PERMISSION')` returns an error if the address calling on the function has not been granted that permission, effectively protecting from malicious use cases. +Because we have initialized the link:https://github.com/aragon/osx-commons/blob/develop/contracts/src/plugin/PluginCloneable.sol[`PluginCloneable` base contract], +we can now use its features, i.e., the link:https://github.com/aragon/osx-commons/blob/1cf46ff15dbda8481f9ee37558e7ea8b257d51f2/contracts/src/permission/auth/DaoAuthorizable.sol#L30-L35[auth modifier] +provided through the `DaoAuthorizable` base class. The `auth('ADMIN_EXECUTE_PERMISSION')` returns an error if the address calling +on the function has not been granted that permission, effectively protecting from malicious use cases. -Later, we will also use the [`dao()` getter function from the base contract](https://github.com/aragon/osx-commons/blob/1cf46ff15dbda8481f9ee37558e7ea8b257d51f2/contracts/src/permission/auth/DaoAuthorizable.sol#L24-L28), which returns the associated DAO for that plugin. +Later, we will also use the link:https://github.com/aragon/osx-commons/blob/1cf46ff15dbda8481f9ee37558e7ea8b257d51f2/contracts/src/permission/auth/DaoAuthorizable.sol#L24-L28[dao() getter function from the base contract], +which returns the associated DAO for that plugin. ```solidity contract SimpleAdmin is PluginCloneable { @@ -69,13 +74,16 @@ contract SimpleAdmin is PluginCloneable { } ``` -:::note -In this example, we are building a governance plugin. To increase its capabilities and provide some standardization into the protocol, we recommend completing the governance plugin by [implementing the `IProposal` and `IMembership` interfaces](../05-governance-plugins/index.md). -Optionally, you can also allow certain actions to fail by using [the failure map feature of the DAO executor](../../../01-how-it-works/01-core/01-dao/01-actions.md#allowing-for-failure). -::: +NOTE: In this example, we are building a governance plugin. To increase its capabilities and provide some standardization into the protocol, we recommend completing the governance plugin by +xref:how-to-guides/plugin-development/governance-plugins/index.adoc[implementing the `IProposal` and `IMembership` interfaces]. +Optionally, you can also allow certain actions to fail by using xref:how-it-works/core/dao/actions.adoc#allowing-for-failure[the failure map feature of the DAO executor]. -For now, we used default values for the `callId` and `allowFailureMap` parameters required by the DAO's `execute` function. With this, the plugin implementation could be used and deployed already. Feel free to add any additional logic to your plugin's capabilities here. +For now, we used default values for the `callId` and `allowFailureMap` parameters required by the DAO's `execute` function. +With this, the plugin implementation could be used and deployed already. Feel free to add any additional logic to +your plugin's capabilities here. ### 3. Plugin done, Setup contract next! -Now that we have the logic for the plugin implemented, we'll need to define how this plugin should be installed/uninstalled from a DAO. In the next step, we'll write the `PluginSetup` contract - the one containing the installation, uninstallation, and upgrading instructions for the plugin. +Now that we have the logic for the plugin implemented, we'll need to define how this plugin should be installed/uninstalled from a DAO. +In the next step, we'll write the `PluginSetup` contract - the one containing the installation, uninstallation, and +upgrading instructions for the plugin. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/index.adoc index de349c40..3b54ae01 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/index.adoc @@ -1,22 +1,22 @@ ---- -title: Non-Upgradeable Plugins ---- += Non-Upgradeable Plugins -## Get Started with Non-Upgradeable Plugins +== Get Started with Non-Upgradeable Plugins A Non-Upgradeable Plugin is a Plugin built on smart contracts that cannot be upgraded. This may or may not be what you want. Some observations: - Non-Upgradeable contracts are simpler to create, deploy, and manage. -- Instantiation is done via the `new` keyword or deployed via the [minimal proxy pattern (ERC-1167)](https://eips.ethereum.org/EIPS/eip-1167) -- The storage is contained within each version. So if your plugin is dependent on state information from previous versions, you won't have access to it directly in upcoming versions, since every version is a blank new state. If this is a requirement for your project, we recommend you deploy an [Upgradeable Plugin](../04-upgradeable-plugin/index.md). +- Instantiation is done via the `new` keyword or deployed via the link:https://eips.ethereum.org/EIPS/eip-1167[minimal proxy pattern (ERC-1167)]. +- The storage is contained within each version. So if your plugin is dependent on state information from previous versions, +you won't have access to it directly in upcoming versions, since every version is a blank new state. If this is a requirement +for your project, we recommend you deploy an xref:how-to-guides/plugin-development/upgradeable-plugin/index.adoc[Upgradeable Plugin]. -Before moving on with the Guide, make sure you've read our documentation on [Choosing the Best Type for Your Plugin](../02-plugin-types.md) to make sure you're selecting the right type of contract for your Plugin. +Before moving on with the Guide, make sure you've read our documentation on xref:how-to-guides/plugin-development/plugin-types.adoc[Choosing the Best Type for Your Plugin] to make sure you're selecting the right type of contract for your Plugin. Up next, check out our guides on: -1. [How to initialize Non-Upgradeable Plugins](./01-initialization.md) -2. [How to build the implementation of a Non-Upgradeable Plugin](./02-implementation.md) -3. [How to build and deploy a Plugin Setup contract for a Non-Upgradeable Plugin](./03-setup.md) -4. [How to publish my plugin into the Aragon OSx protocol](../07-publication/index.md) +1. xref:how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc[How to initialize Non-Upgradeable Plugins] +2. xref:how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc[How to build the implementation of a Non-Upgradeable Plugin] +3. xref:how-to-guides/plugin-development/non-upgradeable-plugin/setup.adoc[How to build and deploy a Plugin Setup contract for a Non-Upgradeable Plugin] +4. xref:how-to-guides/plugin-development/publication/index.adoc[How to publish my plugin into the Aragon OSx protocol] diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc index ac83e5cf..0cbf1290 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/initialization.adoc @@ -1,12 +1,12 @@ ---- -title: Initialization ---- += Initialization -## How to Initialize Non-Upgradeable Plugins +=== How to Initialize Non-Upgradeable Plugins Every plugin should receive and store the address of the DAO it is associated with upon initialization. This is how the plugin will be able to interact with the DAO that has installed it. -In addition, your plugin implementation might want to introduce other storage variables that should be initialized immediately after the contract was created. For example, in the `SimpleAdmin` plugin example (which sets one address as the full admin of the DAO), we'd want to store the `admin` address. +In addition, your plugin implementation might want to introduce other storage variables that should be initialized immediately after the contract was created. +For example, in the `SimpleAdmin` plugin example (which sets one address as the full admin of the DAO), we'd want to store the `admin` +address. ```solidity contract SimpleAdmin is Plugin { @@ -14,18 +14,23 @@ contract SimpleAdmin is Plugin { } ``` -The way we set up the plugin's `initialize()` function depends on the plugin type selected. To review plugin types in depth, check out our [guide here](../02-plugin-types.md). +The way we set up the plugin's `initialize()` function depends on the plugin type selected. To review plugin types in depth, check out +our xref:how-to-guides/plugin-development/plugin-types.adoc[guide here]. -Additionally, the way we deploy our contracts is directly correlated with how they're initialized. For Non-Upgradeable Plugins, there's two ways in which we can deploy our plugin: +Additionally, the way we deploy our contracts is directly correlated with how they're initialized. For Non-Upgradeable Plugins, +there's two ways in which we can deploy our plugin: - Deployment via Solidity's `new` keyword, OR - Deployment via the Minimal Proxy Pattern ### Option A: Deployment via Solidity's `new` Keyword -To instantiate the contract via Solidity's `new` keyword, you should inherit from the `Plugin` Base Template Aragon created. You can find it [here](https://github.com/aragon/osx-commons/blob/develop/contracts/src/plugin/Plugin.sol). +To instantiate the contract via Solidity's `new` keyword, you should inherit from the `Plugin` Base Template Aragon created. +You can find it link:https://github.com/aragon/osx-commons/blob/develop/contracts/src/plugin/Plugin.sol[here]. -In this case, the compiler will force you to write a `constructor` function calling the `Plugin` parent `constructor` and provide it with a contract of type `IDAO`. Inside the constructor, you might want to initialize the storage variables that you have added yourself, such as the `admin` address in the example below. +In this case, the compiler will force you to write a `constructor` function calling the `Plugin` parent `constructor` and +provide it with a contract of type `IDAO`. Inside the constructor, you might want to initialize the storage variables that you have +added yourself, such as the `admin` address in the example below. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -45,15 +50,16 @@ contract SimpleAdmin is Plugin { } ``` -:::note -The `admin` variable is set as `immutable` so that it can never be changed. Immutable variables can only be initialized in the constructor. -::: +NOTE: The `admin` variable is set as `immutable` so that it can never be changed. Immutable variables can only be initialized in +the constructor. -This type of constructor implementation stores the `IDAO _dao` reference in the right place. If your plugin is deployed often, which we could expect, we can [save significant amounts of gas by deployment through using the minimal proxy pattern](https://blog.openzeppelin.com/workshop-recap-cheap-contract-deployment-through-clones/). +This type of constructor implementation stores the `IDAO _dao` reference in the right place. If your plugin is deployed often, w +hich we could expect, we can link:https://blog.openzeppelin.com/workshop-recap-cheap-contract-deployment-through-clones/[save significant amounts of gas by deployment through using the minimal proxy pattern]. ### Option B: Deployment via the Minimal Proxy Pattern -To deploy our plugin via the [minimal clones pattern (ERC-1167)](https://eips.ethereum.org/EIPS/eip-1167), you inherit from the `PluginCloneable` contract introducing the same features as `Plugin`. The only difference is that you now have to remember to write an `initialize` function. +To deploy our plugin via the link:https://eips.ethereum.org/EIPS/eip-1167(minimal clones pattern (ERC-1167)), you inherit from the `PluginCloneable` contract introducing the same features as `Plugin`. +The only difference is that you now have to remember to write an `initialize` function. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -74,8 +80,9 @@ contract SimpleAdmin is PluginCloneable { } ``` -We must protect it from being called multiple times by using [OpenZeppelin's `initializer` modifier made available through `Initializable`](https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable) and call the internal function `__PluginCloneable_init(IDAO _dao)` available through the `PluginCloneable` base contract to store the `IDAO _dao` reference in the right place. +We must protect it from being called multiple times by using link:https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable[OpenZeppelin's `initializer` modifier made available through `Initializable`] and +call the internal function `__PluginCloneable_init(IDAO _dao)` available through the `PluginCloneable` base contract to +store the `IDAO _dao` reference in the right place. -:::caution -If you forget calling `__PluginCloneable_init(_dao)` inside your `initialize` function, your plugin won't be associated with a DAO and cannot use the DAO's `PermissionManager`. -::: +CAUTION: If you forget calling `__PluginCloneable_init(_dao)` inside your `initialize` function, your plugin won't be associated +with a DAO and cannot use the DAO's `PermissionManager`. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/setup.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/setup.adoc index 33940074..d79a06bd 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/setup.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/non-upgradeable-plugin/setup.adoc @@ -1,8 +1,6 @@ ---- -title: Plugin Setup Contract ---- += Plugin Setup Contract -## What is the Plugin Setup contract? +== What is the Plugin Setup contract? The Plugin Setup contract is the contract defining the instructions for installing, uninstalling, or upgrading plugins into DAOs. This contract prepares the permission granting or revoking that needs to happen in order for plugins to be able to perform actions on behalf of the DAO. @@ -132,7 +130,7 @@ contract SimpleAdminSetup is PluginSetup { } ``` -Then, we will use [OpenZeppelin's `Clones` library](https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones) to clone our Plugin contract and initialize it with the `admin` address. The first line, `using Clones for address;`, allows us to call OpenZeppelin `Clones` library to clone contracts deployed at an address. +Then, we will use link:https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones[OpenZeppelin's `Clones` library] to clone our Plugin contract and initialize it with the `admin` address. The first line, `using Clones for address;`, allows us to call OpenZeppelin `Clones` library to clone contracts deployed at an address. The second line introduces a custom error being thrown if the admin address specified is the zero address. @@ -329,10 +327,11 @@ contract SimpleAdminSetup is PluginSetup { } ``` -Once done, our plugin is ready to be published on the Aragon plugin registry. With the address of the `SimpleAdminSetup` contract, we are ready for creating our `PluginRepo`, the plugin's repository where all plugin versions will live. Check out our how to guides on [publishing your plugin here](../07-publication/index.md). +Once done, our plugin is ready to be published on the Aragon plugin registry. With the address of the `SimpleAdminSetup` contract, we are ready for creating our `PluginRepo`, the plugin's repository where all plugin versions will live. +Check out our how to guides on xref:how-to-guides/plugin-development/publication/index.adoc[publishing your plugin here]. ### In the future: Subsequent Builds For subsequent builds or releases of your plugin, you'll simply write a new implementation and associated Plugin Setup contract providing a new `prepareInstallation` and `prepareUninstallation` function. -If a DAO wants to install the new build or release, it must uninstall its current plugin and freshly install the new plugin version, which can happen in the same action array in a governance proposal. However, the plugin storage and event history will be lost since this is a non-upgradeable plugin. If you want to prevent the latter, you can learn [how to write an upgradeable plugin here](../03-non-upgradeable-plugin/index.md). +If a DAO wants to install the new build or release, it must uninstall its current plugin and freshly install the new plugin version, which can happen in the same action array in a governance proposal. However, the plugin storage and event history will be lost since this is a non-upgradeable plugin. If you want to prevent the latter, you can learn xref:how-to-guides/plugin-development/non-upgradeable-plugin/index.adoc[how to write an upgradeable plugin here]. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/plugin-types.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/plugin-types.adoc index 86cd6d8e..f9a4375c 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/plugin-types.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/plugin-types.adoc @@ -1,8 +1,6 @@ ---- -title: Choosing the Plugin Type ---- += Choosing the Plugin Type -## How to Choose the Base Contract for Your Plugin +=== How to Choose the Base Contract for Your Plugin Although it is not mandatory to choose one of our interfaces as the base contracts for your plugins, we do offer some options for you to inherit from and speed up development. @@ -15,8 +13,8 @@ The needs of your plugin determine the type of plugin you may want to choose. Th In this regard, we provide 3 options for base contracts you can choose from: - `Plugin` for instantiation via `new` -- `PluginClones` for [minimal proxy pattern (ERC-1167)](https://eips.ethereum.org/EIPS/eip-1167) deployment -- `PluginUUPSUpgradeable` for [UUPS pattern (ERC-1822)](https://eips.ethereum.org/EIPS/eip-1822) deployment +- `PluginClones` for link:https://eips.ethereum.org/EIPS/eip-1167[minimal proxy pattern (ERC-1167)] deployment. +- `PluginUUPSUpgradeable` for link:https://eips.ethereum.org/EIPS/eip-1822[UUPS pattern (ERC-1822)] deployment. Let's take a look at what this means for you. @@ -37,30 +35,24 @@ Upgradeability and the deployment method of a plugin contract go hand in hand. T > 1. Deploy a new version of the contract > 2. Manually migrate all state from the old one contract to the new one (which can be very expensive in terms of gas fees!) > 3. Update all contracts that interacted with the old contract to use the address of the new one -> 4. Reach out to all your users and convince them to start using the new deployment (and handle both contracts being used simultaneously, as users are slow to migrate -> -> _source: [OpenZeppelin: What's in an upgrade](https://docs.openzeppelin.com/learn/upgrading-smart-contracts#whats-in-an-upgrade)_ +> 4. Reach out to all your users and convince them to start using the new deployment (and handle both contracts being used simultaneously, as users are slow to migrate). + +For more, link:https://docs.openzeppelin.com/learn/upgrading-smart-contracts#whats-in-an-upgrade[See OpenZeppelin's Guide]. Some key things to keep in mind: -- With upgradeable smart contracts, you can modify their code while keep using or even extending the storage (see the guide [Writing Upgradeable Contracts](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable) by OpenZeppelin). +- With upgradeable smart contracts, you can modify their code while keep using or even extending the storage (see the guide link:https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable[Writing Upgradeable Contracts] by OpenZeppelin). - To enable upgradeable smart contracts (as well as cheap contract clones), the proxy pattern is used. -- Depending on your upgradeability requirements and the deployment method you choose, you can also greatly reduce the gas costs to distribute your plugin. However, the upgradeability and deployment method can introduce caveats during [the plugin setup](../../01-how-it-works/02-framework/02-plugin-management/02-plugin-setup/index.md), especially when updating from an older version to a new one. - -The following table compares the different deployment methods with their benefits and drawbacks: - -| | `new` Instantiation | Minimal Proxy (Clones) | Transparent Proxy | UUPS Proxy | -| -------------- | --------------------------------------------- | ------------------------------------------------- | ------------------------------------------------ | --------------------------------------------- | -| upgradeability | no | no | yes | yes | -| gas costs | high | very low | moderate | low | -| difficulty | low | low | high | high | +- Depending on your upgradeability requirements and the deployment method you choose, you can also greatly reduce the gas costs to distribute your plugin. +However, the upgradeability and deployment method can introduce caveats +during xref:how-it-works/framework/plugin-setup/index.adoc[the plugin setup] especially when updating from an older version to a new one. -Accordingly, we recommend to use [minimal proxies (ERC-1167)](https://eips.ethereum.org/EIPS/eip-1167) for non-upgradeable and [UUPS proxies (1822)](https://eips.ethereum.org/EIPS/eip-1822) for upgradeable plugin. +We recommend to use link:https://eips.ethereum.org/EIPS/eip-1167[minimal proxies (ERC-1167)] for non-upgradeable and link:https://eips.ethereum.org/EIPS/eip-1822(UUPS proxies (1822)) for upgradeable plugin. To help you with developing and deploying your plugin within the Aragon infrastructure, we provide the following implementation that you can inherit from: - `Plugin` for instantiation via `new` -- `PluginClones` for [minimal proxy pattern (ERC-1167)](https://eips.ethereum.org/EIPS/eip-1167) deployment -- `PluginUUPSUpgradeable` for [UUPS pattern (ERC-1822)](https://eips.ethereum.org/EIPS/eip-1822) deployment +- `PluginClones` for link:https://eips.ethereum.org/EIPS/eip-1167[minimal proxy pattern (ERC-1167)] deployment. +- `PluginUUPSUpgradeable` for link:https://eips.ethereum.org/EIPS/eip-1822[UUPS pattern (ERC-1822)] deployment. #### Caveats of Non-upgradeable Plugins @@ -68,6 +60,6 @@ Aragon plugins using the non-upgradeable smart contracts bases (`Plugin`, `Plugi Updating, in distinction from upgrading, will call Aragon OSx' internal process for switching from an older plugin version to a newer one. -:::caution -To switch from an older version of a non-upgradeable contract to a newer one, the underlying contract has to be replaced. In consequence, the state of the older version is not available in the new version anymore, unless it is migrated or has been made publicly accessible in the old version through getter functions. -::: +CAUTION: To switch from an older version of a non-upgradeable contract to a newer one, the underlying contract has to be replaced. +In consequence, the state of the older version is not available in the new version anymore, unless it is migrated or has been made +publicly accessible in the old version through getter functions. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/index.adoc index 3f5b3db7..9e86956d 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/index.adoc @@ -1,27 +1,31 @@ ---- -title: Publication of your Plugin into Aragon OSx ---- += Publication of your Plugin into Aragon OSx ## How to publish a plugin into Aragon's plugin registry -Once you've deployed your Plugin Setup contract, you will be able to publish your plugin into Aragon's plugin registry so any Aragon DAO can install it. +Once you've deployed your Plugin Setup contract, you will be able to publish your plugin into Aragon's plugin registry so any +Aragon DAO can install it. ### 1. Make sure your plugin is deployed in the right network -Make sure your Plugin Setup contract is deployed in your network of choice (you can find all of the networks we support [here](https://github.com/aragon/osx-commons/tree/develop/configs/src/deployments/json)). You will need the address of your Plugin Setup contract to be able to publish the plugin into the protocol. +Make sure your Plugin Setup contract is deployed in your network of choice (you can find all of the networks we support link:https://github.com/aragon/osx-commons/tree/develop/configs/src/deployments/json[here]). +You will need the address of your Plugin Setup contract to be able to publish the plugin into the protocol. ### 2. Publishing your plugin -Every plugin in Aragon can have future versions, so when publishing a plugin to the Aragon protocol, we're really creating a [`PluginRepo`](https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol) instance for each plugin, which will contain all of the plugin's versions. +Every plugin in Aragon can have future versions, so when publishing a plugin to the Aragon protocol, we're really creating a link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol[PluginRepo] instance for each plugin, +which will contain all of the plugin's versions. -To publish a plugin, we will use Aragon's `PluginRepoFactory` contract - in charge of creating `PluginRepo` instances containing your plugin's versions. To do this, we will call its `createPluginRepoWithFirstVersion` function, which will [create the first version of a plugin](https://github.com/aragon/core/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepoFactory.sol#L48) and add that new `PluginRepo` address into the `PluginRepoRegistry` containing all available plugins within the protocol. +To publish a plugin, we will use Aragon's `PluginRepoFactory` contract - in charge of creating `PluginRepo` instances containing your plugin's versions. +To do this, we will call its `createPluginRepoWithFirstVersion` function, which will link:https://github.com/aragon/core/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepoFactory.sol#L48[create the first version of a plugin] +and add that new `PluginRepo` address into the `PluginRepoRegistry` containing all available plugins within the protocol. -You can find all of the addresses of `PluginRepoFactory` contracts by network [here](https://github.com/aragon/osx-commons/tree/develop/configs/src/deployments/json). +You can find all of the addresses of `PluginRepoFactory` contracts by network link:https://github.com/aragon/osx-commons/tree/develop/configs/src/deployments/json[here]. -To create more versions of your plugin in the future, you'll call on the [`createVersion` function](https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol#L128) from the `PluginRepo` instance of your plugin. When you publish your plugin, you'll be able to find the address of your plugin's `PluginRepo` instance within the transaction data. +To create more versions of your plugin in the future, you'll call on the link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol#L128[createVersion function] +from the `PluginRepo` instance of your plugin. When you publish your plugin, you'll be able to find the address of your plugin's `PluginRepo` instance within the transaction data. ### 3. Publishing subsequent builds -When publishing subsequent builds, you want to use the `createVersion` function in the `PluginRepo` contract ([check out the function's source code here](https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol#L132)). +When publishing subsequent builds, you want to use the `createVersion` function in the `PluginRepo` contract (link:https://github.com/aragon/osx/blob/develop/packages/contracts/src/framework/plugin/repo/PluginRepo.sol#L132[check out the function's source code here]). -To deploy your plugin, follow the steps in the [`osx-plugin-template-hardhat` README.md](https://github.com/aragon/osx-plugin-template-hardhat/blob/main/README.md#deployment). +To deploy your plugin, follow the steps in the link:https://github.com/aragon/osx-plugin-template-hardhat/blob/main/README.md#deployment[osx-plugin-template-hardhat README.md]. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/metadata.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/metadata.adoc index dcf24c0e..250c0b5c 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/metadata.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/metadata.adoc @@ -1,8 +1,6 @@ ---- -title: Plugin Metadata ---- += Plugin Metadata -## Plugin Metadata Specification +== Plugin Metadata Specification The plugin metadata is necessary to allow the App frontend to interact with any plugins: @@ -25,11 +23,23 @@ It can be replaced at any time with [`updateReleaseMetadata()`](../../../03-refe The `release-metadata.json` file consists of the following entries: -| Key | Type | Description | -| ----------- | ----------- | ---------------------------------------------------------------------------- | -| name | `string` | Name of the plugin (e.g. `"Multisig"`) | -| description | `string` | Description of the plugin release and its functionality. | -| images | UNSPECIFIED | Optional. Contains a series of images advertising the plugins functionality. | +|=== +|Key |Type |Description + +| name +| `string` +| Name of the plugin (e.g. `"Multisig"`) + +| description +| `string` +| Description of the plugin release and its functionality. + +| images +| UNSPECIFIED +| Optional. Contains a series of images advertising the plugins functionality.. + +|=== + #### Example @@ -43,16 +53,28 @@ The `release-metadata.json` file consists of the following entries: ### Build Metadata -The build metadata is a `.json` file stored on IPFS with its IPFS CID published for each build **only once** in the [PluginRepo](../../../01-how-it-works/02-framework/02-plugin-management/01-plugin-repo/index.md) (see also the section about [versioning](../07-publication/01-versioning.md#)). +The build metadata is a `.json` file stored on IPFS with its IPFS CID published for each build **only once** +in the xref:how-it-works/framework/plugin-management/plugin-repo/index.adoc[PluginRepo] (see also the section about xref:how-to-guides/plugin-development/publication/versioning.adoc[versioning]). The intention is to inform about the changes that were introduced in this build compared to the previous one and give instructions to the App frontend and other users on how to interact with the plugin setup and implementation contract. -It can be published **only once** with the call to [`createVersion()`](../../../03-reference-guide/framework/plugin/repo/IPluginRepo.md#external-function-createversion) in `IPluginRepo` by the repo maintainer. +It can be published **only once** with the call to TODO:GIORGI [`createVersion()`](../../../03-reference-guide/framework/plugin/repo/IPluginRepo.md#external-function-createversion) in `IPluginRepo` by the repo maintainer. + +|=== +|Key |Type |Description + +| ui +| UNSPECIFIED +| A special formatted object containing instructions for the App frontend on how to render the plugin's UI. -| Key | Type | Description | -| ----------- | ----------- | --------------------------------------------------------------------------------------------------------- | -| ui | UNSPECIFIED | A special formatted object containing instructions for the App frontend on how to render the plugin's UI. | -| change | `string` | Description of the code and UI changes compared to the previous build of the same release. | -| pluginSetup | `object` | Optional. Contains a series of images advertising the plugins functionality. | +| change +| `string` +| Description of the code and UI changes compared to the previous build of the same release. + +| pluginSetup +| `object` +| Optional. Contains a series of images advertising the plugins functionality. + +|=== Each build metadata contains the following fields: @@ -62,10 +84,20 @@ Each build metadata contains the following fields: Each `"prepare..."` object contains: -| Key | Type | Description | -| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| description | `string` | The description of what this particular setup step is doing and what it requires the input for. | -| inputs | `object[]` | A description of the inputs required for this setup step following the [Solidity JSON ABI](https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--solidity) format enriched with an additional `"description"` field for each element. | +|=== +|Key |Type |Description + +| description +| `string` +| The description of what this particular setup step is doing and what it requires the input for. + +| inputs +| `object[]` +| A description of the inputs required for this setup step following the link:https://docs.ethers.org/v5/api/utils/abi/formats/#abi-formats--solidity[Solidity JSON ABI] format enriched with an additional `"description"` field for each element. + +|=== + + By following the Solidity JSON ABI format for the inputs, we followed an established standard, have support for complex types (tuples, arrays, nested versions of the prior) and allow for future extensibility (such as the human readable description texts that we have added). diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/versioning.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/versioning.adoc index 89348f29..f5722acd 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/versioning.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/publication/versioning.adoc @@ -1,22 +1,23 @@ ---- -title: New Plugin Version ---- += New Plugin Version -## How to add a new version of your plugin +== How to add a new version of your plugin The Aragon OSx protocol has an on-chain versioning system built-in, which distinguishes between releases and builds. -- **Releases** contain breaking changes, which are incompatible with preexisting installations. Updates to a different release are not possible. Instead, you must install the new plugin release and uninstall the old one. -- **Builds** are minor/patch versions within a release, and they are meant for compatible upgrades only (adding a feature or fixing a bug without changing anything else). +- **Releases** contain breaking changes, which are incompatible with preexisting installations. Updates to a different release are +not possible. Instead, you must install the new plugin release and uninstall the old one. +- **Builds** are minor/patch versions within a release, and they are meant for compatible upgrades only +(adding a feature or fixing a bug without changing anything else). Builds are particularly important for `UUPSUpgradeable` plugins, whereas a non-upgradeable plugin will work off of only releases. Given a version tag `RELEASE.BUILD`, we can infer that: +TODO:GIORGI nested list fix. + 1. We are doing a `RELEASE` version when we apply breaking changes affecting the interaction with other contracts on the blockchain to: - The `Plugin` implementation contract such as the - - change or removal of storage variables - removal of external functions - change of external function headers @@ -24,18 +25,14 @@ Given a version tag `RELEASE.BUILD`, we can infer that: 2. We are doing a `BUILD` version when we apply backward compatible changes not affecting the interaction with other contracts on the blockchain to: - The `Plugin` implementation contract such as the - - addition of - - storage variables - external functions - change of - - external function bodies - addition, change, or removal of - - internal functions - constants - immutables @@ -43,17 +40,13 @@ Given a version tag `RELEASE.BUILD`, we can infer that: - errors - The `PluginSetup` contract such as - - addition, change, or removal of - - input parameters - helper contracts - requested permissions - The release and build `metadata` URIs such as the - - change of - - the plugin setup ABI - the plugin UI components - the plugin description diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/implementation.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/implementation.adoc index fa53b80f..dc42a148 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/implementation.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/implementation.adoc @@ -1,8 +1,6 @@ ---- -title: Plugin Implementation Contract ---- += Plugin Implementation Contract -## How to build an Upgradeable Plugin implementation contract +=== How to build an Upgradeable Plugin implementation contract In this guide, we'll build a `SimpleStorage` Upgradeable plugin which all it does is storing a number. @@ -10,13 +8,13 @@ The Plugin contract is the one containing all the logic we'd like to implement o ### 1. Set up the initialize function -Make sure you have the initializer of your plugin well set up. Please review [our guide on how to do that here](./01-initialization.md) if you haven't already. +Make sure you have the initializer of your plugin well set up. Please review xref:how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc[our guide on how to do that here] if you haven't already. Once you this is done, let's dive into several implementations and builds, as can be expected for Upgradeable plugins. ### 2. Adding your plugin implementation logic -In our first build, we want to add an authorized `storeNumber` function to the contract - allowing a caller holding the `STORE_PERMISSION_ID` permission to change the stored value similar to what we did for [the non-upgradeable `SimpleAdmin` Plugin](../03-non-upgradeable-plugin/02-implementation.md): +In our first build, we want to add an authorized `storeNumber` function to the contract - allowing a caller holding the `STORE_PERMISSION_ID` permission to change the stored value similar to what we did for xref:how-to-guides/plugin-development/non-upgradeable-plugin/implementation.adoc[the non-upgradeable `SimpleAdmin` Plugin]. ```solidity import {PluginUUPSUpgradeable, IDAO} '@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol'; diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/index.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/index.adoc index 7c8ba89d..270afa23 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/index.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/index.adoc @@ -1,23 +1,23 @@ ---- -title: Upgradeable Plugins ---- += Upgradeable Plugins -## How to develop an Upgradeable Plugin +== How to develop an Upgradeable Plugin -Upgradeable contracts offer advantages because you can cheaply change or fix the logic of your contract without losing the storage of your contract. If you want to review plugin types in depth, check out our [guide on plugin types here](../02-plugin-types.md). +Upgradeable contracts offer advantages because you can cheaply change or fix the logic of your contract without losing the storage of your contract. +If you want to review plugin types in depth, check out our xref:how-to-guides/plugin-development/plugin-types.adoc[guide on plugin types here]. The drawbacks however, are that: - there are plenty of ways to make a mistake, and - the changeable logic poses a new attack surface. -Although we've abstracted away most of the complications of the upgrade process through our `PluginUUPSUpgradeable` base class, please know that writing an upgradeable contract is an advanced topic. +Although we've abstracted away most of the complications of the upgrade process through our `PluginUUPSUpgradeable` base class, +please know that writing an upgradeable contract is an advanced topic. ### Prerequisites -- You have read about the different [plugin types](../02-plugin-types.md) and decided to develop an upgradeable plugin being deployed via the [UUPS pattern (ERC-1822)](https://eips.ethereum.org/EIPS/eip-1822). +- You have read about the different xref:how-to-guides/plugin-development/plugin-types.adoc[plugin types] and decided to develop an upgradeable plugin being deployed via the link:https://eips.ethereum.org/EIPS/eip-1822[UUPS pattern (ERC-1822)]. - You know how to write a [non-upgradeable plugin](../03-non-upgradeable-plugin/index.md). -- You know about the difficulties and pitfalls of ["Writing Upgradeable Contracts"](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable) that come with +- You know about the difficulties and pitfalls of link:https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable[Writing Upgradeable Contracts] that come with - modifying the storage layout - initialization - inheritance @@ -25,9 +25,9 @@ Although we've abstracted away most of the complications of the upgrade process Up next, check out our guides on: -1. [How to initialize an Upgradeable Plugins](./01-initialization.md) -2. [How to build the implementation of an Upgradeable Plugin](./02-implementation.md) -3. [How to build and deploy a Plugin Setup contract for an Upgradeable Plugin](./03-setup.md) -4. [How to create a subsequent build implementation to an Upgradeable Plugin](./04-subsequent-builds.md) -5. [How to upgrade an Upgradeable Plugin](./05-updating-versions.md) -6. [How to publish my plugin into the Aragon OSx protocol](../07-publication/index.md) +1. xref:how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc[How to initialize an Upgradeable Plugins] +2. xref:how-to-guides/plugin-development/upgradeable-plugin/implementation.adoc[How to build the implementation of an Upgradeable Plugin] +3. xref:how-to-guides/plugin-development/upgradeable-plugin/setup.adoc[How to build and deploy a Plugin Setup contract for an Upgradeable Plugin] +4. xref:how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc[How to create a subsequent build implementation to an Upgradeable Plugin] +5. xref:how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc[How to upgrade an Upgradeable Plugin] +6. xref:how-to-guides/plugin-development/publication/index.adoc[How to publish my plugin into the Aragon OSx protocol] diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc index 1b64cdcb..06329ddb 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/initialization.adoc @@ -1,16 +1,14 @@ ---- -title: Initialization ---- += Initialization -## How to Initialize Upgradeable Plugins +=== How to Initialize Upgradeable Plugins -To deploy your implementation contract via the [UUPS pattern (ERC-1822)](https://eips.ethereum.org/EIPS/eip-1822), you inherit from the `PluginUUPSUpgradeable` contract. +To deploy your implementation contract via the link:https://eips.ethereum.org/EIPS/eip-1822[UUPS pattern (ERC-1822)], you inherit from the `PluginUUPSUpgradeable` contract. -We must protect it from being set up multiple times by using [OpenZeppelin's `initializer` modifier made available through `Initializable`](https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable). In order to do this, we will call the internal function `__PluginUUPSUpgradeable_init(IDAO _dao)` function available through the `PluginUUPSUpgradeable` base contract to store the `IDAO _dao` reference in the right place. +We must protect it from being set up multiple times by using link:https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable[OpenZeppelin's initializer modifier made available through Initializable]. +In order to do this, we will call the internal function `__PluginUUPSUpgradeable_init(IDAO _dao)` function available through the `PluginUUPSUpgradeable` +base contract to store the `IDAO _dao` reference in the right place. -:::note -This has to be called - otherwise, anyone else could call the plugin's initialization with whatever params they wanted. -::: +NOTE: This has to be called - otherwise, anyone else could call the plugin's initialization with whatever params they wanted. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -30,19 +28,20 @@ contract SimpleStorageBuild1 is PluginUUPSUpgradeable { } ``` -:::note -Keep in mind that in order to discriminate between the different initialize functions of your different builds, we name the initialize function `initializeBuild1`. This becomes more demanding for subsequent builds of your plugin. -::: +NOTE: Keep in mind that in order to discriminate between the different initialize functions of your different builds, +we name the initialize function `initializeBuild1`. This becomes more demanding for subsequent builds of your plugin. ### Initializing Subsequent Builds -Since you have chosen to build an upgradeable plugin, you can publish subsequent builds of plugin and **allow the users to update from an earlier build without losing the storage**. +Since you have chosen to build an upgradeable plugin, you can publish subsequent builds of plugin and **allow the users to +update from an earlier build without losing the storage**. -:::caution -Do not inherit from previous versions as this can mess up the inheritance chain. Instead, write self-contained contracts by simply copying the code or modifying the file in your git repo. -::: +CAUTION: Do not inherit from previous versions as this can mess up the inheritance chain. Instead, write self-contained +contracts by simply copying the code or modifying the file in your git repo. -In this example, we wrote a `SimpleStorageBuild2` contract and added a new storage variable `address public account;`. Because users can freshly install the new version or update from build 1, we now have to write two initializer functions: `initializeBuild2` and `initializeFromBuild1` in our Plugin implementation contract. +In this example, we wrote a `SimpleStorageBuild2` contract and added a new storage variable `address public account;`. +Because users can freshly install the new version or update from build 1, we now have to write two initializer +functions: `initializeBuild2` and `initializeFromBuild1` in our Plugin implementation contract. ```solidity /// @title SimpleStorage build 2 @@ -69,8 +68,15 @@ contract SimpleStorageBuild2 is PluginUUPSUpgradeable { } ``` -In general, for each version for which you want to support updates from, you have to provide a separate `initializeFromBuildX` function taking care of initializing the storage and transferring the `helpers` and `permissions` of the previous version into the same state as if it had been freshly installed. +In general, for each version for which you want to support updates from, you have to provide a separate `initializeFromBuildX` +function taking care of initializing the storage and transferring the `helpers` and `permissions` of the previous version into +the same state as if it had been freshly installed. Each `initializeBuildX` must be protected with a modifier that allows it to be only called once. -In contrast to build 1, we now must use [OpenZeppelin's `modifier reinitializer(uint8 build)`](https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable-reinitializer-uint8-) for build 2 instead of `modifier initializer` because it allows us to execute 255 subsequent initializations. More specifically, we used `reinitializer(2)` here for our build 2. Note that we could also have used `function initializeBuild1(IDAO _dao, uint256 _number) external reinitializer(1)` for build 1 because `initializer` and `reinitializer(1)` are equivalent statements. For build 3, we must use `reinitializer(3)`, for build 4 `reinitializer(4)` and so on. +In contrast to build 1, we now must use link:https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable-reinitializer-uint8-[OpenZeppelin's `modifier reinitializer(uint8 build)`] +for build 2 instead of `modifier initializer` because it allows us to execute 255 subsequent initializations. +More specifically, we used `reinitializer(2)` here for our build 2. Note that we could also have used +`function initializeBuild1(IDAO _dao, uint256 _number) external reinitializer(1)` for build 1 because +`initializer` and `reinitializer(1)` are equivalent statements. For build 3, we must use `reinitializer(3)`, +for build 4 `reinitializer(4)` and so on. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/setup.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/setup.adoc index b90f1812..99b52e67 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/setup.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/setup.adoc @@ -1,14 +1,15 @@ ---- -title: Plugin Setup Contract ---- += Plugin Setup Contract -## How to build the Plugin Setup Contract for Upgradeable Plugins +== How to build the Plugin Setup Contract for Upgradeable Plugins -The Plugin Setup contract is the contract defining the instructions for installing, uninstalling, or upgrading plugins into DAOs. This contract prepares the permission granting or revoking that needs to happen in order for plugins to be able to perform actions on behalf of the DAO. +The Plugin Setup contract is the contract defining the instructions for installing, uninstalling, or upgrading plugins into DAOs. +This contract prepares the permission granting or revoking that needs to happen in order for plugins to be able to perform +actions on behalf of the DAO. ### 1. Finish the Plugin contract's first build -Before building the Plugin Setup contract, make sure you have the logic for your plugin implemented. In this case, we're building a simple storage plugin which stores a number. +Before building the Plugin Setup contract, make sure you have the logic for your plugin implemented. In this case, + we're building a simple storage plugin which stores a number. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -36,16 +37,21 @@ contract SimpleStorageBuild1 is PluginUUPSUpgradeable { ### 2. Add the `prepareInstallation()` and `prepareUninstallation()` functions -Each `PluginSetup` contract is deployed only once and each plugin version will have its own `PluginSetup` contract deployed. Accordingly, we instantiate the `implementation` contract via Solidity's `new` keyword as deployment with the minimal proxy pattern would be more expensive in this case. +Each `PluginSetup` contract is deployed only once and each plugin version will have its own `PluginSetup` contract deployed. +Accordingly, we instantiate the `implementation` contract via Solidity's `new` keyword as deployment with the minimal proxy +pattern would be more expensive in this case. -In order for the Plugin to be easily installed into the DAO, we need to define the instructions for the plugin to work effectively. We have to tell the DAO's Permission Manager which permissions it needs to grant or revoke. +In order for the Plugin to be easily installed into the DAO, we need to define the instructions for the plugin to work effectively. +We have to tell the DAO's Permission Manager which permissions it needs to grant or revoke. -Hence, we will create a `prepareInstallation()` function, as well as a `prepareUninstallation()` function. These are the functions the `PluginSetupProcessor.sol` (the contract in charge of installing plugins into the DAO) will use. +Hence, we will create a `prepareInstallation()` function, as well as a `prepareUninstallation()` function. These are the functions +the `PluginSetupProcessor.sol` (the contract in charge of installing plugins into the DAO) will use. The `prepareInstallation()` function takes in two parameters: 1. the `DAO` it should prepare the installation for, and -2. the `_data` parameter containing all the information needed for this function to work properly, encoded as a `bytes memory`. In this case, we get the number we want to store. +2. the `_data` parameter containing all the information needed for this function to work properly, encoded as a `bytes memory`. +In this case, we get the number we want to store. Hence, the first thing we should do when working on the `prepareInstallation()` function is decode the information from the `_data` parameter. Similarly, the `prepareUninstallation()` function takes in a `payload`. @@ -116,15 +122,16 @@ contract SimpleStorageBuild1Setup is PluginSetup { } ``` -As you can see, we have a constructor storing the implementation contract instantiated via the `new` method in the private immutable variable `implementation` to save gas and an `implementation` function to return it. +As you can see, we have a constructor storing the implementation contract instantiated via the `new` method in the private immutable +variable `implementation` to save gas and an `implementation` function to return it. -:::note -Specifically important for this type of plugin is the `prepareUpdate()` function. Since we don't know the parameters we will require when updating the plugin to the next version, we can't add the `prepareUpdate()` function just yet. However, keep in mind that we will need to deploy new Plugin Setup contracts in subsequent builds to add in the `prepareUpdate()` function with each build requirements. We see this in depth in the ["How to update an Upgradeable Plugin" section](./05-updating-versions.md). -::: +NOTE: Specifically important for this type of plugin is the `prepareUpdate()` function. Since we don't know the parameters we will require when updating the plugin to the next version, we can't add the `prepareUpdate()` function just yet. However, keep in mind that we will need to deploy new Plugin Setup contracts in subsequent builds to add in the `prepareUpdate()` function with each build requirements. +We see this in depth in the xref:how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc[How to update an Upgradeable Plugin] section. ### 3. Deployment -Once you're done with your Plugin Setup contract, we'll need to deploy it so we can publish it into the Aragon OSx protocol. You can deploy your contract with a basic deployment script. +Once you're done with your Plugin Setup contract, we'll need to deploy it so we can publish it into the Aragon OSx protocol. +You can deploy your contract with a basic deployment script. Firstly, we'll make sure our preferred network is well setup within our `hardhat.config.js` file, which should look something like: @@ -166,7 +173,8 @@ module.exports = { }; ``` -Then, create a `scripts/deploy.js` file and add a simple deploy script. We'll only be deploying the PluginSetup contract, since this should deploy the Plugin contract within its constructor. +Then, create a `scripts/deploy.js` file and add a simple deploy script. We'll only be deploying the PluginSetup contract, +since this should deploy the Plugin contract within its constructor. ```js import {ethers} from 'hardhat'; @@ -202,4 +210,6 @@ npx hardhat run scripts/deploy.ts ### 4. Publishing the Plugin to the Aragon OSx Protocol -Once done, our plugin is ready to be published on the Aragon plugin registry. With the address of the `SimpleAdminSetup` contract deployed, we're almost ready for creating our `PluginRepo`, the plugin's repository where all plugin versions will live. Check out our how to guides on [publishing your plugin here](../07-publication/index.md). +Once done, our plugin is ready to be published on the Aragon plugin registry. With the address of the `SimpleAdminSetup` +contract deployed, we're almost ready for creating our `PluginRepo`, the plugin's repository where all plugin versions will live. +Check out our how to guides on xref:how-to-guides/plugin-development/publication/index.adoc[publishing your plugin here]. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc index 3568eaaa..44309100 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc @@ -1,27 +1,36 @@ ---- -title: Subsequent Builds ---- += Subsequent Builds -## How to create a subsequent build to an Upgradeable Plugin +== How to create a subsequent build to an Upgradeable Plugin -A build is a new implementation of your Upgradeable Plugin. Upgradeable contracts offer advantages because you can cheaply change or fix the logic of your contract without losing the storage of your contract. +A build is a new implementation of your Upgradeable Plugin. Upgradeable contracts offer advantages because you can cheaply change +or fix the logic of your contract without losing the storage of your contract. The Aragon OSx protocol has an on-chain versioning system built-in, which distinguishes between releases and builds. -- **Releases** contain breaking changes, which are incompatible with preexisting installations. Updates to a different release are not possible. Instead, you must install the new plugin release and uninstall the old one. -- **Builds** are minor/patch versions within a release, and they are meant for compatible upgrades only (adding a feature or fixing a bug without changing anything else). +- **Releases** contain breaking changes, which are incompatible with preexisting installations. Updates to a different release are +not possible. Instead, you must install the new plugin release and uninstall the old one. +- **Builds** are minor/patch versions within a release, and they are meant for compatible upgrades +only (adding a feature or fixing a bug without changing anything else). -In this how to guide, we'll go through how we can create these builds for our plugins. Specifically, we'll showcase two specific types of builds - one that modifies the storage of the plugins, another one which modifies its bytecode. Both are possible and can be implemented within the same build implementation as well. +In this how to guide, we'll go through how we can create these builds for our plugins. Specifically, we'll showcase two specific +types of builds - one that modifies the storage of the plugins, another one which modifies its bytecode. Both are possible and +can be implemented within the same build implementation as well. ### 1. Make sure your previous build is deployed and published -Make sure you have at least one build already deployed and published into the Aragon protocol. Make sure to check out our [publishing guide](../07-publication/index.md) to ensure this step is done. +Make sure you have at least one build already deployed and published into the Aragon protocol. Make sure to check out our +xref:how-to-guides/plugin-development/publication/index.adoc[publishing guide] to ensure this step is done. ### 2. Create a new build implementation -In this second build implementation we want to update the functionality of our plugin - in this case, we want to update the storage of our plugin with new values. Specifically, we will add a second storage variable `address public account;`. Additional to the `initializeFromBuild2` function, we also want to add a second setter function `storeAccount` that uses the same permission as `storeNumber`. +In this second build implementation we want to update the functionality of our plugin - in this case, we want to update +the storage of our plugin with new values. Specifically, we will add a second storage variable `address public account;`. +Additional to the `initializeFromBuild2` function, we also want to add a second setter function `storeAccount` that uses +the same permission as `storeNumber`. -As you can see, we're still inheriting from the `PluginUUPSUpgradeable` contract and simply overriding some implementation from the previous build. The idea is that when someone upgrades the plugin and calls on these functions, they'll use this new upgraded implementation, rather than the older one. +As you can see, we're still inheriting from the `PluginUUPSUpgradeable` contract and simply overriding some implementation +from the previous build. The idea is that when someone upgrades the plugin and calls on these functions, they'll use this +new upgraded implementation, rather than the older one. ```solidity import {PluginUUPSUpgradeable, IDAO} '@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol'; @@ -60,15 +69,16 @@ contract SimpleStorageBuild2 is PluginUUPSUpgradeable { } ``` -Builds that you publish don't necessarily need to introduce new storage variables of your contracts and don't necessarily need to change the storage. To read more about Upgradeability, check out [OpenZeppelin's UUPSUpgradeability implementation here](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable). +Builds that you publish don't necessarily need to introduce new storage variables of your contracts and don't necessarily need to +change the storage. To read more about Upgradeability, check out link:https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable[OpenZeppelin's UUPSUpgradeability implementation here]. -:::note -Note that because these contracts are Upgradeable, keeping storage gaps `uint256 [50] __gap;` in dependencies is a must in order to avoid storage corruption. To learn more about storage gaps, review OpenZeppelin's documentation [here](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps). -::: +NOTE: Note that because these contracts are Upgradeable, keeping storage gaps `uint256 [50] __gap;` in dependencies is a must in +order to avoid storage corruption. To learn more about storage gaps, review OpenZeppelin's documentation link:https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps[here]. ### 3. Alternatively, a build implementation modifying bytecode -Updates for your contracts don't necessarily need to affect the storage, they can also modify the plugin's bytecode. Modifying the contract's bytecode, means making changes to: +Updates for your contracts don't necessarily need to affect the storage, they can also modify the plugin's bytecode. +Modifying the contract's bytecode, means making changes to: - functions - constants @@ -76,10 +86,15 @@ Updates for your contracts don't necessarily need to affect the storage, they ca - events - errors -For this third build, then, we want to change the bytecode of our implementation as an example, so we 've introduced two separate permissions for the `storeNumber` and `storeAccount` functions and named them `STORE_NUMBER_PERMISSION_ID` and `STORE_ACCOUNT_PERMISSION_ID` permission, respectively. Additionally, we decided to add the `NumberStored` and `AccountStored` events as well as an error preventing users from setting the same value twice. All these changes only affect the contract bytecode and not the storage. +For this third build, then, we want to change the bytecode of our implementation as an example, so we 've introduced two +separate permissions for the `storeNumber` and `storeAccount` functions and named them `STORE_NUMBER_PERMISSION_ID` and `STORE_ACCOUNT_PERMISSION_ID` permission, respectively. +Additionally, we decided to add the `NumberStored` and `AccountStored` events as well as an error preventing users from setting the +same value twice. All these changes only affect the contract bytecode and not the storage. -Here, it is important to remember how Solidity stores `constant`s (and `immutable`s). In contrast to normal variables, they are directly written into the bytecode on contract creation so that we don't need to worry that the second `bytes32` constant that we added shifts down the storage so that the value in `uint256 public number` gets lost. -It is also important to note that, the `initializeFromBuild2` could be left empty. Here, we just emit the events with the currently stored values. +Here, it is important to remember how Solidity stores `constant`s (and `immutable`s). In contrast to normal variables, they are directly +written into the bytecode on contract creation so that we don't need to worry that the second `bytes32` constant that we added +shifts down the storage so that the value in `uint256 public number` gets lost. It is also important to note that, the `initializeFromBuild2` +could be left empty. Here, we just emit the events with the currently stored values. ```solidity import {PluginUUPSUpgradeable, IDAO} '@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol'; @@ -145,6 +160,5 @@ contract SimpleStorageBuild3 is PluginUUPSUpgradeable { } ``` -:::note -Despite no storage-related changes happening in build 3, we must apply the `reinitializer(3)` modifier to all `initialize` functions so that none of them can be called twice or in the wrong order. -::: +NOTE: Despite no storage-related changes happening in build 3, we must apply the `reinitializer(3)` modifier to all `initialize` functions so that +none of them can be called twice or in the wrong order. diff --git a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc index c21935e6..2c3157a9 100644 --- a/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc +++ b/components/osx/modules/ROOT/pages/how-to-guides/plugin-development/upgradeable-plugin/updating-versions.adoc @@ -1,16 +1,16 @@ ---- -title: Upgrade a DAO Plugin ---- += Upgrade a DAO Plugin ## How to upgrade an Upgradeable Plugin -Updating an Upgradeable plugin means we want to direct the implementation of our functionality to a new build, rather than the existing one. +Updating an Upgradeable plugin means we want to direct the implementation of our functionality to a new build, rather than +the existing one. In this tutorial, we will go through how to update the version of an Upgradeable plugin and each component needed. ### 1. Create the new build implementation contract -Firstly, you want to create the new build implementation contract the plugin should use. You can read more about how to do this in the ["How to create a subsequent build implementation to an Upgradeable Plugin" guide](./04-subsequent-builds.md). +Firstly, you want to create the new build implementation contract the plugin should use. You can read more about how to do +this in the xref:how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc[How to create a subsequent build implementation to an Upgradeable Plugin] guide. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -54,9 +54,13 @@ contract SimpleStorageBuild2 is PluginUUPSUpgradeable { ### 2. Write a new Plugin Setup contract -In order to do update a plugin, we need a `prepareUpdate()` function in our Plugin Setup contract which points the functionality to a new build, as we described in the ["How to create a subsequent build implementation to an Upgradeable Plugin" guide](./04-subsequent-builds.md). The `prepareUpdate()` function must transition the plugin from the old build state into the new one so that it ends up having the same permissions (and helpers) as if it had been freshly installed. +In order to do update a plugin, we need a `prepareUpdate()` function in our Plugin Setup contract which points the functionality to a +new build, as we described in the xref:how-to-guides/plugin-development/upgradeable-plugin/subsequent-builds.adoc[How to create a subsequent build implementation to an Upgradeable Plugin] guide. +The `prepareUpdate()` function must transition the plugin from the old build state into the new one so that it ends up having the +same permissions (and helpers) as if it had been freshly installed. -In contrast to the original build 1, build 2 requires two input arguments: `uint256 _number` and `address _account` that we decode from the bytes-encoded input `_data`. +In contrast to the original build 1, build 2 requires two input arguments: `uint256 _number` and `address _account` that we decode +from the bytes-encoded input `_data`. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -146,7 +150,10 @@ contract SimpleStorageBuild2Setup is PluginSetup { } ``` -The key thing to review in this new Plugin Setup contract is its `prepareUpdate()` function. The function only contains a condition checking from which build number the update is transitioning to build `2`. Here, it is the build number `1` as this is the only update path we support. Inside, we decode the `address _account` input argument provided with `bytes _date` and pass it to the `initializeFromBuild1` function taking care of initializing the storage that was added in this build. +The key thing to review in this new Plugin Setup contract is its `prepareUpdate()` function. The function only contains a +condition checking from which build number the update is transitioning to build `2`. Here, it is the build number `1` as this is the +only update path we support. Inside, we decode the `address _account` input argument provided with `bytes _data` and pass +it to the `initializeFromBuild1` function taking care of initializing the storage that was added in this build. ### 3. Future builds @@ -154,8 +161,7 @@ For each build we add, we will need to add a `prepareUpdate()` function with any In this third build, for example, we are modifying the bytecode of the plugin. -
-Third plugin build example, modifying the plugin's bytecode +**Third plugin build example, modifying the plugin's bytecode.** ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -224,12 +230,11 @@ contract SimpleStorageBuild3 is PluginUUPSUpgradeable { } ``` -
-With each new build implementation, we will need to update the Plugin Setup contract to be able to update to that new version. We do this through updating the `prepareUpdate()` function to support any new features that need to be set up. +With each new build implementation, we will need to update the Plugin Setup contract to be able to update to that new version. +We do this through updating the `prepareUpdate()` function to support any new features that need to be set up. -
-Third plugin setup example, modifying prepareUpdate function +**Third plugin setup example, modifying `prepareUpdate` function**. ```solidity // SPDX-License-Identifier: AGPL-3.0-or-later @@ -349,12 +354,16 @@ contract SimpleStorageBuild3Setup is PluginSetup { } ``` -
-In this case, the `prepareUpdate()` function only contains a condition checking from which build number the update is transitioning to build 2. Here, we can update from build 0 or build 1 and different operations must happen for each case to transition to `SimpleAdminBuild3`. +In this case, the `prepareUpdate()` function only contains a condition checking from which build number the update is transitioning +to build 2. Here, we can update from build 0 or build 1 and different operations must happen for each case to transition to +`SimpleAdminBuild3`. -In the first case, `initializeFromBuild1` is called taking care of initializing `address _account` that was added in build 1 and emitting the events added in build 2. +In the first case, `initializeFromBuild1` is called taking care of initializing `address _account` that was added in build 1 and +emitting the events added in build 2. In the second case, `initializeFromBuild2` is called taking care of initializing the build. Here, only the two events will be emitted. -Lastly, the `prepareUpdate()` function takes care of modifying the permissions by revoking the `STORE_PERMISSION_ID` and granting the more specific `STORE_NUMBER_PERMISSION_ID` and `STORE_ACCOUNT)PERMISSION_ID` permissions, that are also granted if build 2 is freshly installed. This must happen for both update paths so this code is outside the `if` statements. +Lastly, the `prepareUpdate()` function takes care of modifying the permissions by revoking the `STORE_PERMISSION_ID` and granting +the more specific `STORE_NUMBER_PERMISSION_ID` and `STORE_ACCOUNT_PERMISSION_ID` permissions, that are also granted if build 2 is +freshly installed. This must happen for both update paths so this code is outside the `if` statements.