Dynamic Permissions for Organization "Actions" with Signer Integration

In order for a DAO to interact with external applications and protocols the DAO must have some way to perform an arbitrary external action and some way to ensure that the arbitrary actions can be integrated into the DAOs internal governance process.

This capability would drastically enhance the utility of a DAO by allowing the DAO and its members to collectively participate in any web3 application, including other DAOs. For example a DAO might choose to participate in a staking pool, breed cryptokitties, or participate in a larger DAO. This post is intended to discuss a possible way to implement such functionality and identify specific design challenges that might arise as a result.

From a usability perspective, a member of the DAO would ideally interact with an external application or protocol as they normally would using a web3 provider/browser, but when performing the action that intent should bridged to the DAO and trigger the transaction pathing workflow they experience when interacting with a native Aragon Application. At a high level this seems relatively simple, you need the signer to be aware of which DAO the user wants to perform the action as, you need a way to take actions at the signer level and dynamically produce an intent, and you need an application within the DAO that processes those intents.

Signer Integration

In order for DAOs to perform web3 actions, there needs to be support at web3 provider or signer level. This would allow a user to register the DAO as an available account in the signer, and when performing an action the signer would dynamically generate an “intent” for the action and open the DAO where the client could process the “intent” normally.

In order for the DAO to process the intent a new application would need to be used to manage permissions for dynamically generated intents.

Managing Permissions of External Actions

We cannot assume that external actions will be aware of the ACL or have permission modifiers since they are simply function calls on an external contract. From the DAOs perspective these external actions can be divided into actions which have been registered in the DAO’s ACL and actions which are currently unregistered. This results in the following possible permissions:

  1. Execute an unregistered action
  2. Register an external action in the ACL
  3. Execute a registered action

Perhaps this could be accomplished by creating a generic “connector” that serves as the DAOs interface to external contracts.

Potential Issues/Concerns

Many web3 actions interact with the balances of assets held by the function caller, or send assets to the callers address. From the DAOs perspective the assets should generally be held in the organizations “vault”. A simple approach might be to create a version of the vault that implements ability to execute external actions described above. However, adding additional functionality, particularly open ended functionality like executing arbitrary actions to the vault may be risky.

In practice this would mean the vault would act more like a collective wallet, than it does currently.

5 Likes

It occurred to me that some of the challenges related to the vault might be similar to the challenges related to Gnosis two-factor authentication/multi-sig project described here: https://blog.gnosis.pm/the-state-of-storing-funds-on-ethereum-fdb4c9a09388

Right now every single forwarder is capable of doing this, including the voting contract and the token manager. All that needs to happen is integration with a web3 provider like meta mask. These contracts are not able to act on behalf of the vault, but that is what protects the funds in the vault. They can receive and act as a controller of erc20 tokens.

1 Like

These contracts are not able to act on behalf of the vault, but that is what protects the funds in the vault.

It seems the ACL and the organizations permissions/governance process are what protect the funds in the vault, not whether the vault implements a forwarding interface or not. Keep in mind that the ability to use the vaults forwarder could simply be disabled entirely by the ACL.

They can receive and act as a controller of erc20 tokens.

If an organization has multiple voting applications (because they need different configurations of quorum, or each instance is associated with different groups within the organization) this becomes problematic. Consider the following case:

An organization has a some ETH in its Vault. It has two relevant groups of user, its general membership represented by an organizational token, and a group of elected administrators who are granted specific permissions within the organization. A controversial event occurs in the ethereum ecosystem, and there is a carbon vote that the organization would like to participate in. In order to participate with all of its voting weight, it needs to move all of the funds out of the vault to another contract, then from that contract place the vote, and if it moves any of the funds back to the vault after, the then the vote won’t be counted.

This is a specific example, but any situation where asset balances are used in an application but not necessarily transferred is likely to have similar issues. This seems to be the reason for allowing the actions to be performed on behalf of the vault, but I agree that even without that capability a signer level integration would still be tremendously useful.

1 Like

I think there’s a super easy way of making this happen following the next architecture:

  • Deploy a Forwarder which implements a single role (like https://github.com/jvluso/aragon-identity/blob/master/contracts/Identity.sol)
  • Deploy it with a fixed address, e.g. it can forward any payload but just to a particular address or set of addresses. Pretty sure we can do this with the ACL already?
  • Then you can deploy multiple instances of the app, each being able to forward to a different address
  • You can then even implement some frontend logic that identifies what’s the kind of entity you’re interacting with, and display if it’s a multisig/cryptokitties/etc. using the same Entity component we already have in Aragon UI
1 Like

Very interesting proposal! I think I understand the problem trying to be solved here and that having this capability would be a huge advantage, but I guess I still need to think a little bit more about it. Meanwhile I have some questions/comments:

  • At the contracts level, I don’t see any problem as long as we don’t need delegatecalls. As it appears in that chart at the end of the article by Gnosis you linked above, there’s a trade-off between convenience and security. It’s a broad problem and we have faced it before, so after having that recommendation by two different audit teams, we decided to remove delegatecall from EVMScripts, see here and here. Not sure if there is any use case that needs it.
    We could just call newVote from DAO’s Voting app, crafting a script to call the external contract as needed.
  • At the client level, I guess is a little bit more complex. As far as I understand, Web3 providers/signers would have to replicate part of the process we are doing when forwarding actions in Aragon Dapp. I guess I would let aside the transaction path finding. We don’t know if that external contract has ACLs or any other protection mechanism, but we should assume that the designated app in the DAO (let’s say Voting) has permission to perform that action on the external contract, otherwise it would fail (yes, bad UX, but I don’t think there’s much we can do here). The provider should detect that we are using a DAO, learn which is the designated app (e.g., Voting), which is the function to be used (e.g. newVote), craft the script to be passed to that function in our specific format and then ask the user to sign the call to it. Besides it would be good that the content of that script calling the external contract would have a Radspec description to be able to present meaningful information to users.
    I see too much specific Aragon stuff here, so I guess first thing we should do would be to propose a Standard for forwarding, so there’s a call like getDefaultForwarderApp (which in our case would be something similar to our getRecoveryVault() but for this other purpose) and this app should implement something similar to our IForwarder interface which should be part of the standard too. (The signer would then call forward which in turn would call newVote if Voting is the default forwarding app). Besides, we should standardize and promote Radspec too, and hope that all external contracts implement it.
    Then we could expect all providers/signers to implement this functionality. I good candidate for start trying such a solution would be Frame.
2 Likes

I feel like this might be a bit unrealistic to expect providers to build in awareness of the DAO architecture, especially initially. I’m imagining a more limited integration where users register specific addresses which they would like to be able to interact with from the signer.

Also I think in most cases, people probably want a single address that represents the DAO as an entity. For example DAO-A might be a participant in DAO-B, in DAO-B’s ACL they would probably want to reference DAO-A as a DAO entity in their ACL and not a specific instance of DAO-A’s voting app.

1 Like

I agree about that unrealistic providers implementation, at least generally speaking.
About your second paragraph:

  • I agree also that it’s better to reference a DAO than a particular app of it. That could be solved wrapping the forward call from the Kernel. The Kernel would then call getDefaultForwarderApp to know which app has to redirect the action too. This would also save half of the work to the provider, but there would still be the problem of crafting the script.
  • can we then assume that both DAO-A and DAO-B are Aragon DAO’s? That makes things easier for us. I thought the idea was to be able to interact with any external entity.

The only function that will ever need to be used for this is the forward function. This makes creating a standard easier. The provider will hopefully be able to create an account that can be provided from the chain of apps to forward the command through. Then the ACL only has to be able to create that chain and pass it to the provider.

While I agree that most of the time you will want to talk about the DAO and not the app of the DAO, I don’t think that will always be the case, and I don’t think that we should be forcing that. Some DAOs are going to want investors to interact with a different part than customers, or for decisions made by leadership to not be able to do the things that decisions made by membership can. For the DAOs that do only want one way to interact with the outside chain, it isn’t hard to create an identity app and only use that.

1 Like

Yes, I completely agree. Actually, it was added a little bit below, but now I realize that the way I explained it was little bit confusing.

Agree with this sentiment.

For the DAOs that do only want one way to interact with the outside chain, it isn’t hard to create an identity app and only use that.

Im wondering though if this “identity” app should actually be a vault like application with a forwarder given how many applications may be built with the expectation that the users address will contain tokens.

Amazing thread.

Following the aragonOS design philosophy, I think this should be a new Aragon app that can be installed in DAOs and can represent the DAO externally with the ability of performing arbitrary calls to contracts. I have been thinking about this as an ‘Actor’ app, as it performs actions on behalf of the DAO. ‘Identity’ is not a bad name, though I can see it being conflicted with aragon-id and other notions of identity people have; and ‘connector’ (how we have been internally referring this concept for a while) is very vague. Of course naming is not the most important topic, but would like to get some feedback so we can start precisely referring to it.

As @luis was mentioning, a DAO could have and control as many actors as desired, by installing new instances of the app in the DAO. However I think limiting one actor’s influence to just one external address is an unnecessary limitation (for example, it wouldn’t allow an actor interacting with a governance token and a voting app because they will probably have different addresses).

The reason why an actor is valuable and worth protecting behind the ACL is because of the references to the account address in the state in other contracts grant that account some rights (e.g. balance in a token contract, ownership of a powerful contract, voting power). Some of these rights are transferrable and the way to authenticate transfers is by performing an external call from this contract. In the context of the actor app, sometimes it will need to receive and transfer valuable assets in order to perform some actions (e.g. needs to receive tokens that then stakes to get voting rights), all valuable assets DAOs own right now are owned by a Vault.

There were some comments about whether to make the DAO Kernel or regular Vaults capable of performing arbitrary actions. I don’t think that’s a good idea for the Vault contract, as it is a pretty critical contract to make arbitrary external calls from (calls moving tokens or ETH could slip through the cracks) and I’m always hesitant to add more logic to the Kernel as there can only be one per DAO (though I like @bingen’s idea of having a default instance of this app linked in the Kernel)

The Actor app could be made a derivative of the current Vault app, inheriting the ability to manage ETH and tokens with the current roles, which could then have a reference to a ‘parent vault’ where funds could transferred to (the generic recover-to-vault mechanism shouldn’t be used as it is callable by anyone). The rationale is that when the Actor app needs to hold valuable assets, some funds can be transferred from a main vault and then have a way for these funds to be pulled back to the vault (which wouldn’t be a very critical action as the funds would go to the vault). Depending on the use case, some DAOs may decide to make the Actor app their main Vault where funds are held and actions are made from.

As for the permissions of the app, I think for now we can start by just having one main role for performing actions, which can be parametrized with the ACL for limiting interacting with certain addresses or functions if needed. If this is not enough, we can then move to a scheme similar to what @lkngtn proposes here:

Making this component a standard Forwarder that executes EVMScripts (as in @jvluso’s implementation) would be tricky as we wouldn’t be able to limit what addresses and functions can be interacted with, unless major changes were made to the way we execute scripts or introduce script inspection in the app (which wouldn’t scale if other script executors are installed in the DAO). I propose that we execute calls directly from this app rather than using aragonOS scripts. Given that:

  • This wouldn’t be an issue for interacting with external contracts that aren’t aragonOS-compatible, as transaction pathing would need to be assisted by the signer component anyway.
  • Not being able to perform many actions is not really an issue since in most cases the Actor app will be run from a forwarder that can execute scripts. E.g. the Voting app (forwarder) can execute a script that hits an actor app instance multiple time.
  • The only aspect that we would be limiting by not executing EVMScripts directly would be future inter-DAO transaction pathing for executing actions in other DAOs.

I coded a prototype illustrating all the ideas explained above, looking forward to hearing some feedback as this seemingly straight-forward app is way more opinionated than expected.

4 Likes

I completely agree with your reply @jorge. Super insightful. I love the term Actor as well!

Trying to wrap my head around this particular case, If a DAO is participating in another DAO but does not have direct permissions (but may have permissions through a forwarder such as the voting app), how would that DAO execute that action in this scenario?

Would it be essentially be like this:

  1. User registers the “actors” they wish to masquerade as in the signer
  2. When they attempt to perform the action the signer passes the action to the “actor” and the user interacts with the application within the internal context that the relevant “actor” belongs to directly

Does this break when you get a second level of nested permissions, for example, DAO-A is a member of DAO-B which is a member of DAO-C. Alice is an individual in DAO-A and has no individual authority in DAO-B or DAO-C, to trigger an action in DAO-C she needs to traverse both DAO-A and DAO-B. If she tries to masquerade as DAO-B directly, her credentials will be rejected by DAO-B’s ACL. If she tries to masquerade as DAO-A, DAO-A does not have permission in DAO-C. It seems she would need to masquerade as DAO-B on behalf of DAO-A, which starts to be complex unless the path can be calculated on the users behalf. I’m a bit worried that this type of transaction pathing might add lots of complexity that is very much dependent on the Aragon architecture/ACL to the signer.

This also brings up another issue, which could perhaps be a separate thread, right now the signer view in the dapp is designed to directly interact with a web3 providers, which is a nice UX if the user uses a web3 enabled wallet, but provides no option for interaction with wallets that don’t necessarily support that type of interaction. If the signer has the option to export payload data so that it can be signed offline, that might solve some of these edge cases. If the “Actor” app also allowed for the execution of payload data like this, then that could be used in these edge cases that might be too complex to incorporate directly into a signing provider.

The app I am working on for credential recovery is going to be an app where pathing a transaction through multiple DAOs is necessary. Let’s use an example where three people, A, B, and C each want to be able to recover each others credentials. A, B, and C will each need separate DAOs, and a fourth DAO will be necessary to combine them. That means that when A wants their credentials reset, B will have to create a transaction that starts in DAO B, goes to the combined DAO, and then calls a function in DAO A. The easiest way for this to happen is for the signer to be able to read all three ACLs to create a chain of forwarded transactions.

I also think it should be possible to do forwarding requirements e.g. https://gist.github.com/jvluso/204ebd630840ae126773da7a052da559

2 Likes

Transaction pathing between DAOs that needs more than one hop is going to need some support either from the user directly or from some sort of caching server that knows the relationships between all existing DAOs.

In my current prototype for the Actor app that is not a forwarder, the data that this ‘export’ would provide you is exactly what you need to send through the actor. I can imagine a simple UI for this app which would be similar to mycrypto.com’s ‘Contracts’ tab, in which you provide the ABI of a contract and specify which function you want to call and its arguments, and also an alternative way to input the target and the calldata directly.

This case would be exactly the same as what Luke is describing, DAO A being C in Luke’s example, the recovery DAO being B and DAO B being A, where the user (member of DAO B) wants to call the recover function in DAO A that needs two hops. As I said before, transaction pathing requiring more than 1 hop between DAOs is not trivial at all.

I agree with @jvluso on the fact that making the Actor app a regular forwarder feels cleaner and closer to how Aragon apps are generally architected, but I feel that having the ability to have different entities be able to perform different external actions from the same Actor will be very important and inspecting an arbitrary EVMScript is impossible.

I think for the purpose of this thread focus on the ability to interact with other protocols and contracts which haven’t been built thinking about aragonOS or the ACL and discuss multi-DAO interactions separately as it is a very deep rabbit hole!

3 Likes

We should consider implementing EIP 1271 so the Actor app could ‘sign messages’ for interacting with other contracts like 0x. Two (compatible) ways to add support for it could be:

  • Having an explicit action to sign a specific data hash, like what I proposed here. The function to sign a hash in the Actor app needs to be protected with a role. Once a hash is ‘signed’ in the app, it will return true to isValidSignature(data, [any signature or empty])

  • Potentially using the same role as before, if an entity has permissions to sign on behalf of the Actor app, the entity can provide a signature directly with its account. When checking the signature, the Actor app can recover the account that signed the message and if that account is allowed to sign messages on behalf of the Actor app, then consider it a valid signature.

1 Like

I think that design-wise this is what @jorge’s proposal could look like:

You would see all the past actions that happened and its date. Also you would see the current balance of the Actor.
When creating a new action, you would have a panel with a form with:

  • An entity component to write the address you want to interact with
  • An ABI selector, similar to the one in MyCrypto
  • Or a toggle to just copy and paste the call data
1 Like

One thing worth considering here is the risk of signature invalidation attacks when integrating some external protocol with a DAO that supports designated signers.

What I mean by signature invalidation is a malicious DAO that designates a signer for the Actor that creates a signature for some message in the external protocol. At some later point, the external protocol needs to validate the signature for some reason i.e. a party wants to use the DAO’s signature as proof of commitment to some past data. The DAO could change the designated signer for the Actor such that when the external protocol checks the original signature, it will think that the signature is invalid (because the designated signer is now different).

I think this is a general problem that applies to designated signers for any contract. For example, in Casper FFG, a validator could double vote by signing conflicting vote messages, but before someone can submit the signatures as a fraud proof on-chain, the validator could modify its validation contract such that one of the signatures becomes invalid. The solution in this case was to use a purity checker to make sure that the validation contract could not update state to invalidate a signature after it has been created.

5 Likes

@yondonfu Regarding the purity checker, it seems that it would need to ban DELEGATECALL (actually all types of calls) to make sure a contract is ‘pure’. This would present a challenge with our current architecture as we are thinking that these Actor app instances will be upgradeable contracts, therefore making it impossible to prove that a signature was actually valid at some point in the past.

When I was reading your message I initially thought you were referring to another potential issue that is when the delegated signer sees that they are going to be removed, they can still sign a message and perform an action somewhere front-running the transaction that removes their power.

2 Likes