-
-
Save izqui/54875e896838be894a8a4a9cab52f86e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma experimental ABIEncoderV2; | |
// Some fake interfaces so it compiles | |
interface IACL { | |
function initialize(address permissionsCreator) external; | |
function createPermission(address _entity, address _app, bytes32 _role, address _manager) external; | |
function grantPermission(address _entity, address _app, bytes32 _role) external; | |
function hasPermission(address who, address where, bytes32 what, bytes calldata how) external view returns (bool); | |
} | |
interface ERCProxy {} | |
interface ERC20 { | |
function transfer(address to, uint256 amount) external returns (bool); | |
} | |
interface Agent { | |
function initialize() external; | |
} | |
interface IKernel { | |
function acl() external view returns (IACL); | |
function hasPermission(address who, address where, bytes32 what, bytes calldata how) external view returns (bool); | |
function newAppInstance(bytes32 _appId, address _appBase, bytes calldata _initializePayload, bool _setDefault) external returns (ERCProxy appProxy); | |
function setApp(bytes32 namespace, bytes32 appId, address app) external; | |
function getApp(bytes32 namespace, bytes32 appId) external view returns (address); | |
} | |
contract AragonApp { | |
modifier auth(string memory role) {_;} | |
modifier onlyInit {_;} | |
function kernel() public view returns (IKernel); | |
} | |
// App that would be installed in DAOs that want to use one transaction/vote installs | |
contract AppCenter is AragonApp { | |
struct RegisteredInstall { | |
InstallerBase installer; // Perhaps allow an array of installers as well (in case the installer code is too big) | |
uint256 nextInstall; | |
bytes32[] callHashes; | |
} | |
RegisteredInstall[] public registeredInstalls; | |
event Installed(address indexed installer, bytes data); | |
event Registered(uint256 installId, address indexed installer, bytes[] calls); | |
function executeInstaller(InstallerBase installer, bytes calldata data) auth("EXECUTE_INSTALLER") external { | |
_executeInstaller(installer, data); | |
} | |
// Allows multi-transaction | |
function registerInstall(InstallerBase installer, bytes[] calldata calls) auth("REGISTER_INSTALLS") external returns (uint256 installId) { | |
bytes32[] memory callHashes = new bytes32[](calls.length); | |
for (uint256 i = 0; i < calls.length; i++) { | |
callHashes[i] = keccak256(abi.encodePacked(calls[i])); | |
} | |
installId = registeredInstalls.push(RegisteredInstall(installer, 0, callHashes)) - 1; | |
emit Registered(installId, address(installer), calls); | |
} | |
// Unprotected function | |
function executeRegisteredInstall(uint256 installId, uint256 callId, bytes calldata data) external { | |
require(registeredInstalls.length > installId); | |
RegisteredInstall storage registeredInstall = registeredInstalls[installId]; | |
require(registeredInstall.nextInstall == callId); | |
require(registeredInstall.callHashes[callId] == keccak256(abi.encodePacked(data))); | |
registeredInstall.nextInstall += 1; | |
_executeInstaller(registeredInstall.installer, data); | |
} | |
function _executeInstaller(InstallerBase installer, bytes memory data) internal { | |
(bool ok,) = address(installer).delegatecall(data); | |
require(ok); | |
emit Installed(address(installer), data); | |
} | |
} | |
// Example installer for installing Agent into an org | |
contract InstallerBase is AragonApp { | |
function getBaseFromAPM(bytes32 appId) internal returns (address) { | |
return address(0); // TODO | |
} | |
} | |
contract AgentInstaller is InstallerBase { | |
bytes32 internal constant AGENT_APP_ID = 0x00; | |
bytes32 internal constant AGENT_EXEC_ROLE = 0x00; | |
ERC20 public constant ANT = ERC20(0); | |
uint256 public constant DEV_FEE = 10 ether; // 10 ANT | |
address public constant AGENT_DEV = address(0); | |
function install(address[] calldata allowedExecutors, address permissionManager) external onlyInit { | |
IKernel dao = kernel(); | |
IACL acl = dao.acl(); | |
Agent agent = Agent(address(dao.newAppInstance(AGENT_APP_ID, getBaseFromAPM(AGENT_APP_ID), new bytes(0), false))); | |
agent.initialize(); | |
for (uint256 i = 0; i < allowedExecutors.length; i++) { | |
if (i == 0) { | |
acl.createPermission(allowedExecutors[i], address(agent), AGENT_EXEC_ROLE, permissionManager); | |
} else { | |
// Even if the installer is not the manager, AppCenter is 'permissionManagerRoot' in the ACL (to be implemented) so it can grant the permission regardless | |
acl.grantPermission(allowedExecutors[i], address(agent), AGENT_EXEC_ROLE); | |
} | |
} | |
// Developers that create installers for easy installs can charge a fee | |
// Funds would be sent to the AppCenter app to pay for installs or a transfer from Finance can be done | |
// in the same vote before triggering the install | |
require(ANT.transfer(AGENT_DEV, DEV_FEE)); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I loved the idea! Looking forward to see more installers :)
Some comments/ideas:
callId
from outside inexecuteRegisteredInstall
, right? See here