Skip to content

Commit

Permalink
feat: add/remove extensions + set extension acl flags (#376)
Browse files Browse the repository at this point in the history
* add extension configuration as well

* make it possible for Managing to update extensions too

* test: added test cases

* fix test and extension creator issue

* docs: updated managing adapter docs

* chore: bump to version 1.0.1

Co-authored-by: Felipe Forbeck <felipe.forbeck@gmail.com>
  • Loading branch information
adridadou and fforbeck committed Sep 9, 2021
1 parent 912d843 commit 72b966d
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 176 deletions.
88 changes: 51 additions & 37 deletions contracts/adapters/Managing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ contract ManagingContract is
MemberGuard,
AdapterGuard
{
struct ProposalDetails {
bytes32 adapterId;
address adapterAddress;
bytes32[] keys;
uint256[] values;
uint128 flags;
}

mapping(address => mapping(bytes32 => ProposalDetails)) public proposals;

/*
Expand All @@ -63,51 +55,49 @@ contract ManagingContract is
* @dev keys and value must have the same length.
* @dev proposalId can not be reused.
* @param dao The dao address.
* @param proposalId The guild kick proposal id.
* @param proposal proposal details
* @param proposalId Tproposal details
* @param proposal The proposal details
* @param data Additional data to pass to the voting contract and identify the submitter
*/
function submitProposal(
DaoRegistry dao,
bytes32 proposalId,
ProposalInput memory proposal,
bytes32[] memory keys,
uint256[] memory values,
bytes memory data
ProposalDetails calldata proposal,
bytes calldata data
) external override onlyMember(dao) reentrancyGuard(dao) {
require(
keys.length == values.length,
proposal.keys.length == proposal.values.length,
"must be an equal number of config keys and values"
);

require(proposal.flags < type(uint128).max, "flags parameter overflow");
require(
proposal.extensionAddresses.length ==
proposal.extensionAclFlags.length,
"must be an equal number of extension addresses and acl"
);

require(proposal.flags < type(uint128).max, "proposal flags overflow");

require(
isNotReservedAddress(proposal.adapterAddress),
"adapter address is reserved address"
isNotReservedAddress(proposal.adapterOrExtensionAddr),
"address is reserved"
);

dao.submitProposal(proposalId);

proposals[address(dao)][proposalId] = ProposalDetails(
proposal.adapterId,
proposal.adapterAddress,
keys,
values,
proposal.flags
);
proposals[address(dao)][proposalId] = proposal;

IVoting votingContract = IVoting(dao.getAdapterAddress(VOTING));

dao.sponsorProposal(
proposalId,
address senderAddress =
votingContract.getSenderAddress(
dao,
address(this),
data,
msg.sender
),
address(votingContract)
);
);

dao.sponsorProposal(proposalId, senderAddress, address(votingContract));
votingContract.startNewVotingForProposal(dao, proposalId, data);
}

Expand Down Expand Up @@ -136,12 +126,36 @@ contract ManagingContract is
);

dao.processProposal(proposalId);
dao.replaceAdapter(
proposal.adapterId,
proposal.adapterAddress,
proposal.flags,
proposal.keys,
proposal.values
);
if (proposal.updateType == UpdateType.ADAPTER) {
dao.replaceAdapter(
proposal.adapterOrExtensionId,
proposal.adapterOrExtensionAddr,
proposal.flags,
proposal.keys,
proposal.values
);
} else if (proposal.updateType == UpdateType.EXTENSION) {
if (dao.extensions(proposal.adapterOrExtensionId) != address(0x0)) {
dao.removeExtension(proposal.adapterOrExtensionId);
}

if (proposal.adapterOrExtensionAddr != address(0x0)) {
dao.addExtension(
proposal.adapterOrExtensionId,
IExtension(proposal.adapterOrExtensionAddr),
dao.getMemberAddress(0)
);
}
} else {
revert("unknown update type");
}

for (uint128 i = 0; i < proposal.extensionAclFlags.length; i++) {
dao.setAclToExtensionForAdapter(
proposal.extensionAddresses[i],
proposal.adapterOrExtensionAddr,
proposal.extensionAclFlags[i]
);
}
}
}
19 changes: 12 additions & 7 deletions contracts/adapters/interfaces/IManaging.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,24 @@ SOFTWARE.
*/

interface IManaging {
struct ProposalInput {
bytes32 adapterId;
address adapterAddress;
enum UpdateType {UNKNOWN, ADAPTER, EXTENSION}

struct ProposalDetails {
bytes32 adapterOrExtensionId;
address adapterOrExtensionAddr;
UpdateType updateType;
uint128 flags;
bytes32[] keys;
uint256[] values;
address[] extensionAddresses;
uint128[] extensionAclFlags;
}

function submitProposal(
DaoRegistry dao,
bytes32 proposalId,
ProposalInput memory proposal,
bytes32[] memory keys,
uint256[] memory values,
bytes memory data
ProposalDetails calldata proposal,
bytes calldata data
) external;

function processProposal(DaoRegistry dao, bytes32 proposalId) external;
Expand Down
89 changes: 43 additions & 46 deletions docs/adapters/Managing.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,50 @@
## Adapter description and scope

The Managing adapter handles the proposal creation, sponsorship and processing of a new adapter including its initial configuration, and permissions.
The Managing adapter handles the proposal creation, sponsorship and processing of a new adapter/extension including its initial configuration, and permissions.

An adapter can be added, removed or replaced in the DAO registry. In order to remove an adapter one must pass the address 0x0 with the adapter id that needs to be removed. In order to add a new adapter one most provide the adapter id, address and access flags. The address of the new adapter can not be a reserved address, and the id must be a known id as defined in the `DaoConstants.sol`. The replace adapter operation removes the adapter from the registry based on the adapter id parameter, and also adds a new adapter using the same id but with a new address.
An adapter/extension can be added, removed or replaced in the DAO registry. In order to remove an adapter/extension one must pass the address 0x0 with the adapter/extension id that needs to be removed. To add a new adapter/extension one most provide the adapter/extension id, address and access flags. The address of the new adapter/extension can not be a reserved address, and the id must be a known id as defined in the `DaoConstants.sol`. The replace adapter/extension operation removes the adapter/extension from the registry based on the id parameter, and also adds a new adapter/extension using the same id but with a new address.

The Managing adapter also allows one set custom ACL flags to adapters that need to communicate with different extensions.

:::caution
It is important to indicate which operation type will be performed. Set 1 to `updateType` when you are adding/removing an `Adapter`, and set 2 when you are adding/removing an `Extension`.
:::

## Adapter workflow

Submit a proposal and check:

- if caller is an active member
- if keys and values have equal length
- if adapter address is valid
- if adapter/extension address is valid
- if the access flags don't overflow
- if adapter address is not reserved
- if adapter/extension address is not reserved

If all the requirements pass, then the proposal is subitted to registry and the adapter stores the proposal data.

To sponsor a proposal, you need to be an active member, and once sponsored the voting process starts.

Once the voting period ends, only a member can process the proposal. The proposal is processed only if:
Once the voting period ends, anyone can process the proposal. The proposal is processed only if:

- the caller is an active member
- the has not been processed already
- it has not been processed already
- the proposal has been sponsored
- the voting has passed
- the updateType is 1 (adapter) or 2 (extension)
- the extension `AclFlags`, and `address` are valid

## Adapter configuration

DAORegistry Access Flags: `SUBMIT_PROPOSAL`, `REPLACE_ADAPTER`.
DAORegistry Access Flags: `SUBMIT_PROPOSAL`, `REPLACE_ADAPTER`, `ADD_EXTENSION`, `REMOVE_EXTENSION` .

## Adapter state

- `proposals`: All the proposals handled by the adapter per DAO.
- `ProposalDetails`:
- `adapterId`: The id of the adapter to add, remove or replace.
- `adapterAddress`: The address of the new adapter contract.
- `adapterOrExtensionId`: The id of the adapter/extension to add, remove or replace.
- `adapterOrExtensionAddr`: The contract address of the adapter/extension.
- `flags`: The DAO ACL for the new adapter.
- `keys`: The configuration keys for the adapter.
- `values`: The values to set for the adapter configuration.
- `flags`: The ACL for the new adapter.
- `extensionAddresses`: The list of extension addresses that the adapter interacts with.
- `extensionAclFlags`: The list of ACL flags that the adapter needs to interact with each extension.

## Dependencies and interactions (internal / external)

Expand Down Expand Up @@ -65,39 +72,16 @@ DAORegistry Access Flags: `SUBMIT_PROPOSAL`, `REPLACE_ADAPTER`.
* @dev keys and value must have the same length.
* @dev proposalId can not be reused.
* @param dao The dao address.
* @param proposalId The guild kick proposal id.
* @param adapterId The adapter id to replace, remove or add.
* @param adapterAddress The adapter address to add or replace. Use 0x0 if you want to remove the adapter.
* @param keys The configuration keys for the adapter.
* @param values The values to set for the adapter configuration.
* @param _flags The ACL for the new adapter, up to 2**128-1.
* @param proposalId Tproposal details
* @param proposal The proposal details
* @param data Additional data to pass to the voting contract and identify the submitter
*/
function submitProposal(
DaoRegistry dao,
bytes32 proposalId,
bytes32 adapterId,
address adapterAddress,
bytes32[] calldata keys,
uint256[] calldata values,
uint256 _flags
) external override onlyMember(dao)
```

### function sponsorProposal

```solidity
/**
* @notice Sponsor a proposal if the proposal id exists.
* @dev Only members are allowed to sponsor proposals.
* @param dao The dao address.
* @param proposalId The guild kick proposal id.
* @param data Additional data that can be used for offchain voting validation.
*/
function sponsorProposal(
DaoRegistry dao,
bytes32 proposalId,
ProposalDetails calldata proposal,
bytes calldata data
) external override onlyMember(dao)
) external override onlyMember(dao) reentrancyGuard(dao)
```

### function processProposal
Expand All @@ -114,12 +98,25 @@ DAORegistry Access Flags: `SUBMIT_PROPOSAL`, `REPLACE_ADAPTER`.
function processProposal(DaoRegistry dao, bytes32 proposalId)
external
override
onlyMember(dao)
reentrancyGuard(dao)
```

## Events

- `AdapterRemoved`: when an adapter is removed from the regitry. Event emitted by the DAO Registry.
- `AdapterAdded`: when a new adapter is added to the registry. Event emitted by the DAO Registry.
- `ConfigurationUpdated`: when a new configuration is stored in the registry. Event emitted by the DAO Registry.
### AdapterRemoved

When an adapter is removed from the regitry. Event emitted by the DAO Registry.

- `event AdapterRemoved(bytes32 adapterId);`

### AdapterAdded

When a new adapter is added to the registry. Event emitted by the DAO Registry.

- `event AdapterAdded(bytes32 adapterId, address adapterAddress, uint256 flags);`

### ConfigurationUpdated

When a new configuration is stored in the registry. Event emitted by the DAO Registry.

- `event ConfigurationUpdated(bytes32 key, uint256 value);`
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tribute-contracts",
"version": "1.0.0-rc2",
"version": "1.0.2",
"description": "A new modular DAO framework, inspired by the Moloch smart contracts",
"main": "truffle-config.js",
"keywords": [
Expand Down
26 changes: 16 additions & 10 deletions test/adapters/guildkick.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -705,13 +705,16 @@ describe("Adapter - GuildKick", () => {
this.dao.address,
getProposalCounter(),
{
adapterId: newAdapterId,
adapterAddress: newAdapterAddress,
adapterOrExtensionId: newAdapterId,
adapterOrExtensionAddr: newAdapterAddress,
flags: 0,
updateType: 1,
keys: [],
values: [],
extensionAddresses: [],
extensionAclFlags: [],
},
[],
[],
[],
{ from: kickedMember, gasPrice: toBN("0") }
),
"onlyMember"
Expand Down Expand Up @@ -766,20 +769,23 @@ describe("Adapter - GuildKick", () => {

const proposalId = getProposalCounter();
//Submit a new Bank adapter proposal
let newadapterId = sha3("onboarding");
let newadapterAddress = accounts[3]; //TODO deploy some Banking test contract
let newAdapterId = sha3("onboarding");
let newAdapterAddress = accounts[3]; //TODO deploy some Banking test contract
await expectRevert(
managing.submitProposal(
this.dao.address,
proposalId,
{
adapterId: newadapterId,
adapterAddress: newadapterAddress,
adapterOrExtensionId: newAdapterId,
adapterOrExtensionAddr: newAdapterAddress,
flags: 0,
updateType: 1,
keys: [],
values: [],
extensionAddresses: [],
extensionAclFlags: [],
},
[],
[],
[],
{ from: kickedMember, gasPrice: toBN("0") }
),
"onlyMember"
Expand Down

0 comments on commit 72b966d

Please sign in to comment.