├── contracts ├── .npmignore ├── test │ ├── helpers │ │ ├── EthSender.sol │ │ ├── ProxyTarget.sol │ │ ├── ThrowProxy.sol │ │ └── ACLHelper.sol │ ├── mocks │ │ ├── common │ │ │ ├── EtherTokenConstantMock.sol │ │ │ ├── Uint256Mock.sol │ │ │ ├── LifecycleMock.sol │ │ │ ├── InitializableStorageMock.sol │ │ │ ├── DepositableStorageMock.sol │ │ │ ├── TimeHelpersMock.sol │ │ │ ├── ReentrancyGuardMock.sol │ │ │ └── KeccakConstants.sol │ │ ├── evmscript │ │ │ ├── EVMScriptRegistryConstantsMock.sol │ │ │ ├── EVMScriptExecutorNoReturnMock.sol │ │ │ ├── EVMScriptExecutorRevertMock.sol │ │ │ ├── ExecutionTarget.sol │ │ │ └── EVMScriptExecutorMock.sol │ │ ├── lib │ │ │ ├── standards │ │ │ │ └── ERC165Mock.sol │ │ │ ├── misc │ │ │ │ └── ERCProxyMock.sol │ │ │ ├── math │ │ │ │ ├── SafeMath8Mock.sol │ │ │ │ └── SafeMath64Mock.sol │ │ │ └── token │ │ │ │ ├── SafeERC20Mock.sol │ │ │ │ ├── TokenMock.sol │ │ │ │ ├── TokenReturnMissingMock.sol │ │ │ │ └── TokenReturnFalseMock.sol │ │ ├── kernel │ │ │ ├── KernelSetAppMock.sol │ │ │ ├── UpgradedKernel.sol │ │ │ ├── KernelDepositableMock.sol │ │ │ ├── KernelConstantsMock.sol │ │ │ └── KernelOverloadMock.sol │ │ ├── apps │ │ │ ├── VaultMock.sol │ │ │ ├── UnsafeAragonAppMock.sol │ │ │ ├── AragonAppMock.sol │ │ │ ├── AppStubConditionalRecovery.sol │ │ │ ├── AppStorageMock.sol │ │ │ ├── disputable │ │ │ │ ├── AgreementMock.sol │ │ │ │ └── DisputableAppMock.sol │ │ │ ├── AppStubDepositable.sol │ │ │ ├── DepositableDelegateProxyMock.sol │ │ │ ├── AppStub.sol │ │ │ ├── AppProxyPinnedStorageMock.sol │ │ │ └── AppStubScriptRunner.sol │ │ └── forwarding │ │ │ └── ForwardingMock.sol │ └── tests │ │ ├── TestDelegateProxySelfDestruct.sol │ │ └── TestDelegateProxy.sol ├── kernel │ ├── KernelStorage.sol │ ├── IKernel.sol │ ├── KernelConstants.sol │ └── KernelProxy.sol ├── acl │ ├── IACLOracle.sol │ ├── IACL.sol │ └── ACLSyntaxSugar.sol ├── evmscript │ ├── IEVMScriptExecutor.sol │ ├── executors │ │ ├── BaseEVMScriptExecutor.sol │ │ └── CallsScript.sol │ ├── IEVMScriptRegistry.sol │ ├── ScriptHelpers.sol │ ├── EVMScriptRegistry.sol │ └── EVMScriptRunner.sol ├── common │ ├── EtherTokenConstant.sol │ ├── Uint256Helpers.sol │ ├── Autopetrified.sol │ ├── IVaultRecoverable.sol │ ├── DepositableStorage.sol │ ├── IsContract.sol │ ├── Petrifiable.sol │ ├── ReentrancyGuard.sol │ ├── DelegateProxy.sol │ ├── UnstructuredStorage.sol │ ├── ConversionHelpers.sol │ ├── TimeHelpers.sol │ ├── VaultRecoverable.sol │ ├── Initializable.sol │ └── DepositableDelegateProxy.sol ├── lib │ ├── misc │ │ ├── ERCProxy.sol │ │ └── Migrations.sol │ ├── math │ │ ├── Math.sol │ │ ├── SafeMath8.sol │ │ ├── SafeMath64.sol │ │ └── SafeMath.sol │ ├── standards │ │ └── ERC165.sol │ └── token │ │ └── ERC20.sol ├── apps │ ├── IAragonApp.sol │ ├── UnsafeAragonApp.sol │ ├── AppProxyUpgradeable.sol │ ├── AppStorage.sol │ ├── disputable │ │ ├── IDisputable.sol │ │ └── IAgreement.sol │ ├── AppProxyBase.sol │ ├── AppProxyPinned.sol │ └── AragonApp.sol ├── forwarding │ ├── IForwarderFee.sol │ ├── IForwarderWithContext.sol │ ├── IForwarder.sol │ ├── IForwarderPayable.sol │ ├── IForwarderWithContextPayable.sol │ └── IAbstractForwarder.sol └── factory │ ├── EVMScriptRegistryFactory.sol │ ├── AppProxyFactory.sol │ └── DAOFactory.sol ├── .gitattributes ├── .soliumignore ├── codechecks.yml ├── test ├── contracts │ ├── acl │ │ └── acl_interpreter.js │ ├── common │ │ ├── conversion_helpers.js │ │ ├── delegate_proxy.js │ │ ├── uint256_helpers.js │ │ ├── time_helpers.js │ │ ├── lifecycle.js │ │ ├── reentrancy_guard.js │ │ ├── unstructured_storage.js │ │ └── keccak_constants.js │ ├── apps │ │ ├── app_interface.js │ │ ├── app_base_lifecycle.js │ │ ├── app_funds.js │ │ └── app_acl.js │ ├── lib │ │ └── math │ │ │ ├── lib_safemath8.js │ │ │ └── lib_safemath64.js │ ├── kernel │ │ ├── kernel_upgrade.js │ │ └── kernel_funds.js │ └── forwarding │ │ └── forwarding.js └── helpers │ ├── permissionParams.js │ ├── errors.js │ └── runSolidityTest.js ├── .github ├── release-drafter.yml ├── auto_assign.yml ├── config.yml └── workflows │ └── ci_contracts.yml ├── migrations └── 1_initial_migration.js ├── MIGRATION.md ├── .solcover.js ├── .gitignore ├── SECURITY.md ├── .soliumrc.json ├── package.json ├── readme.md ├── buidler.config.js └── CONTRIBUTING.md /contracts/.npmignore: -------------------------------------------------------------------------------- 1 | /test 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/lib 3 | contracts/test -------------------------------------------------------------------------------- /codechecks.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | - name: eth-gas-reporter/codechecks 3 | -------------------------------------------------------------------------------- /test/contracts/acl/acl_interpreter.js: -------------------------------------------------------------------------------- 1 | const runSolidityTest = require('../../helpers/runSolidityTest') 2 | 3 | runSolidityTest('TestACLInterpreter') 4 | -------------------------------------------------------------------------------- /test/contracts/common/conversion_helpers.js: -------------------------------------------------------------------------------- 1 | const runSolidityTest = require('../../helpers/runSolidityTest') 2 | 3 | runSolidityTest('TestConversionHelpers') 4 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Configuration for https://probot.github.io/apps/release-drafter 2 | 3 | template: | 4 | ## What’s changed in aragonOS 5 | 6 | $CHANGES 7 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require('Migrations.sol') 2 | 3 | module.exports = (deployer) => { 4 | deployer.deploy(Migrations) 5 | } 6 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migrating aragonOS versions 2 | 3 | ## aragonOS 3 --> aragonOS 4 4 | 5 | See the [aragonOS 4 migration guide](https://hack.aragon.org/docs/aragonos-4-migration.html). 6 | -------------------------------------------------------------------------------- /contracts/test/helpers/EthSender.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | contract EthSender { 5 | function sendEth(address to) external payable { 6 | to.transfer(msg.value); 7 | } 8 | } -------------------------------------------------------------------------------- /test/contracts/common/delegate_proxy.js: -------------------------------------------------------------------------------- 1 | const runSolidityTest = require('../../helpers/runSolidityTest') 2 | 3 | runSolidityTest('TestDelegateProxy') 4 | runSolidityTest('TestDelegateProxySelfDestruct') 5 | -------------------------------------------------------------------------------- /contracts/kernel/KernelStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | contract KernelStorage { 5 | // namespace => app id => address 6 | mapping (bytes32 => mapping (bytes32 => address)) public apps; 7 | bytes32 public recoveryVaultAppId; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/acl/IACLOracle.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | interface IACLOracle { 9 | function canPerform(address who, address where, bytes32 what, uint256[] how) external view returns (bool); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/EtherTokenConstantMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/EtherTokenConstant.sol"; 4 | 5 | 6 | contract EtherTokenConstantMock is EtherTokenConstant { 7 | function getETHConstant() external pure returns (address) { return ETH; } 8 | } 9 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | 'lib', 4 | 'test', 5 | 'acl/ACLSyntaxSugar.sol', 6 | ], 7 | mocha: { 8 | grep: '@skip-on-coverage', // Find everything with this tag 9 | invert: true // Run the grep's inverse set. 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/Uint256Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/Uint256Helpers.sol"; 4 | 5 | 6 | contract Uint256Mock { 7 | using Uint256Helpers for uint256; 8 | 9 | function convert(uint256 a) public pure returns (uint64) { 10 | return a.toUint64(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # yarn 5 | yarn-error.log 6 | 7 | # Ignore package-lock files (only use yarn.lock) 8 | package-lock.json 9 | 10 | # coverage 11 | .coverage* 12 | coverage/ 13 | coverageEnv/ 14 | coverage.json 15 | allFiredEvents 16 | scTopics 17 | 18 | # build artifacts 19 | abi 20 | cache 21 | artifacts 22 | -------------------------------------------------------------------------------- /.github/auto_assign.yml: -------------------------------------------------------------------------------- 1 | # Configuration for https://probot.github.io/apps/auto-assign 2 | 3 | addReviewers: true 4 | addAssignees: false 5 | 6 | reviewers: 7 | - sohkai 8 | - facuspagnuolo 9 | - izqui 10 | 11 | skipKeywords: 12 | - wip 13 | - draft 14 | 15 | # Set 0 to add all the reviewers (default: 0) 16 | numberOfReviewers: 0 17 | -------------------------------------------------------------------------------- /contracts/evmscript/IEVMScriptExecutor.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | interface IEVMScriptExecutor { 9 | function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes); 10 | function executorType() external pure returns (bytes32); 11 | } 12 | -------------------------------------------------------------------------------- /contracts/test/mocks/evmscript/EVMScriptRegistryConstantsMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../evmscript/IEVMScriptRegistry.sol"; 4 | 5 | 6 | contract EVMScriptRegistryConstantsMock is EVMScriptRegistryConstants { 7 | function getEVMScriptRegistryAppId() external pure returns (bytes32) { return EVMSCRIPT_REGISTRY_APP_ID; } 8 | } 9 | -------------------------------------------------------------------------------- /contracts/common/EtherTokenConstant.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | // aragonOS and aragon-apps rely on address(0) to denote native ETH, in 9 | // contracts where both tokens and ETH are accepted 10 | contract EtherTokenConstant { 11 | address internal constant ETH = address(0); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/evmscript/executors/BaseEVMScriptExecutor.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../../common/Autopetrified.sol"; 8 | import "../IEVMScriptExecutor.sol"; 9 | 10 | 11 | contract BaseEVMScriptExecutor is IEVMScriptExecutor, Autopetrified { 12 | uint256 internal constant SCRIPT_START_LOCATION = 4; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/test/helpers/ProxyTarget.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract ProxyTargetWithoutFallback { 4 | event Pong(); 5 | 6 | function ping() external { 7 | emit Pong(); 8 | } 9 | } 10 | 11 | contract ProxyTargetWithFallback is ProxyTargetWithoutFallback { 12 | event ReceivedEth(); 13 | 14 | function () external payable { 15 | emit ReceivedEth(); 16 | } 17 | } -------------------------------------------------------------------------------- /contracts/test/mocks/common/LifecycleMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/Initializable.sol"; 4 | import "../../../common/Petrifiable.sol"; 5 | 6 | 7 | contract LifecycleMock is Initializable, Petrifiable { 8 | function initializeMock() public { 9 | initialized(); 10 | } 11 | 12 | function petrifyMock() public { 13 | petrify(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/lib/misc/ERCProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | contract ERCProxy { 9 | uint256 internal constant FORWARDING = 1; 10 | uint256 internal constant UPGRADEABLE = 2; 11 | 12 | function proxyType() public pure returns (uint256 proxyTypeId); 13 | function implementation() public view returns (address codeAddr); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/standards/ERC165Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../lib/standards/ERC165.sol"; 4 | 5 | 6 | contract ERC165Mock is ERC165 { 7 | bytes4 public constant ERC165_INTERFACE = ERC165_INTERFACE_ID; 8 | 9 | function interfaceID() external pure returns (bytes4) { 10 | ERC165 erc165; 11 | return erc165.supportsInterface.selector; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/common/Uint256Helpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library Uint256Helpers { 5 | uint256 private constant MAX_UINT64 = uint64(-1); 6 | 7 | string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; 8 | 9 | function toUint64(uint256 a) internal pure returns (uint64) { 10 | require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); 11 | return uint64(a); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/acl/IACL.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | interface IACL { 9 | function initialize(address permissionsCreator) external; 10 | 11 | // TODO: this should be external 12 | // See https://github.com/ethereum/solidity/issues/4832 13 | function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/InitializableStorageMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/Initializable.sol"; 4 | 5 | 6 | contract InitializableStorageMock is Initializable { 7 | function initialize() onlyInit public { 8 | initialized(); 9 | } 10 | 11 | function getInitializationBlockPosition() public pure returns (bytes32) { 12 | return INITIALIZATION_BLOCK_POSITION; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/test/mocks/kernel/KernelSetAppMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../kernel/Kernel.sol"; 4 | 5 | contract KernelSetAppMock is Kernel { 6 | constructor() Kernel(false) public { 7 | } 8 | 9 | // Overloaded mock to bypass the auth and isContract checks 10 | function setApp(bytes32 _namespace, bytes32 _appId, address _app) public { 11 | apps[_namespace][_appId] = _app; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/apps/IAragonApp.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../kernel/IKernel.sol"; 8 | 9 | 10 | contract IAragonApp { 11 | // Includes appId and kernel methods: 12 | bytes4 internal constant ARAGON_APP_INTERFACE_ID = bytes4(0x54053e6c); 13 | 14 | function kernel() public view returns (IKernel); 15 | function appId() public view returns (bytes32); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/common/Autopetrified.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./Petrifiable.sol"; 8 | 9 | 10 | contract Autopetrified is Petrifiable { 11 | constructor() public { 12 | // Immediately petrify base (non-proxy) instances of inherited contracts on deploy. 13 | // This renders them uninitializable (and unusable without a proxy). 14 | petrify(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/DepositableStorageMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/DepositableStorage.sol"; 4 | 5 | 6 | contract DepositableStorageMock is DepositableStorage { 7 | function setDepositableExt(bool _depositable) public { 8 | setDepositable(_depositable); 9 | } 10 | 11 | function getDepositablePosition() public pure returns (bytes32) { 12 | return DEPOSITABLE_POSITION; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/common/IVaultRecoverable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | interface IVaultRecoverable { 9 | event RecoverToVault(address indexed vault, address indexed token, uint256 amount); 10 | 11 | function transferToVault(address token) external; 12 | 13 | function allowRecoverability(address token) external view returns (bool); 14 | function getRecoveryVault() external view returns (address); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test/mocks/kernel/UpgradedKernel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../kernel/Kernel.sol"; 4 | 5 | 6 | contract UpgradedKernel is Kernel { 7 | constructor(bool _shouldPetrify) Kernel(_shouldPetrify) public {} 8 | 9 | // just adds one more function to the kernel implementation. 10 | // calling this function on the previous instance will fail 11 | function isUpgraded() public pure returns (bool) { 12 | return true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for https://probot.github.io/apps/welcome 2 | 3 | newIssueWelcomeComment: > 4 | Thanks for opening your first issue in aragonOS! Someone will circle back soon ⚡ 5 | 6 | newPRWelcomeComment: > 7 | Thanks for opening this pull request! Someone will review it soon 🔍 8 | 9 | firstPRMergeComment: > 10 | Congrats on merging your first pull request! Aragon is proud of you 🦅 11 | 12 | ![Eagle gif](https://media.giphy.com/media/SLD8eKFPuUDuw/200w_d.gif) 13 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/misc/ERCProxyMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../lib/misc/ERCProxy.sol"; 4 | 5 | 6 | contract ERCProxyMock is ERCProxy { 7 | uint256 public constant FORWARDING = 1; 8 | uint256 public constant UPGRADEABLE = 2; 9 | 10 | function proxyType() public pure returns (uint256 proxyTypeId) { 11 | return 0; 12 | } 13 | 14 | function implementation() public view returns (address codeAddr) { 15 | return address(0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/VaultMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/UnsafeAragonApp.sol"; 4 | import "../../../common/DepositableStorage.sol"; 5 | 6 | 7 | contract VaultMock is UnsafeAragonApp, DepositableStorage { 8 | event LogFund(address sender, uint256 amount); 9 | 10 | function initialize() external { 11 | initialized(); 12 | setDepositable(true); 13 | } 14 | 15 | function () external payable { 16 | emit LogFund(msg.sender, msg.value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/UnsafeAragonAppMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/UnsafeAragonApp.sol"; 4 | import "../../../kernel/IKernel.sol"; 5 | 6 | 7 | contract UnsafeAragonAppMock is UnsafeAragonApp { 8 | function initialize() public { 9 | initialized(); 10 | } 11 | 12 | function getKernel() public view returns (IKernel) { 13 | return kernel(); 14 | } 15 | 16 | function setKernelOnMock(IKernel _kernel) public { 17 | setKernel(_kernel); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/helpers/permissionParams.js: -------------------------------------------------------------------------------- 1 | const { bn } = require('@aragon/contract-helpers-test') 2 | 3 | const permissionParamEqOracle = (oracleAddress) => { 4 | // Set role such that the Oracle canPerform() function is used to determine the permission 5 | const argId = '0xCB' // arg 203 - Oracle ID 6 | const op = '01' // equal 7 | const value = oracleAddress.slice(2).padStart(60, 0) // 60 as params are uint240 8 | return bn(`${argId}${op}${value}`) 9 | } 10 | 11 | module.exports = { 12 | permissionParamEqOracle 13 | } 14 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AragonAppMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AragonApp.sol"; 4 | 5 | 6 | contract AragonAppMock is AragonApp { 7 | bytes4 public constant ARAGON_APP_INTERFACE = ARAGON_APP_INTERFACE_ID; 8 | 9 | function initialize() external { 10 | initialized(); 11 | } 12 | 13 | function interfaceID() external pure returns (bytes4) { 14 | IAragonApp iAragonApp; 15 | return iAragonApp.kernel.selector ^ 16 | iAragonApp.appId.selector; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/mocks/kernel/KernelDepositableMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/DepositableStorage.sol"; 4 | import "../../../kernel/Kernel.sol"; 5 | 6 | contract KernelDepositableMock is Kernel, DepositableStorage { 7 | constructor(bool _shouldPetrify) Kernel(_shouldPetrify) public { 8 | } 9 | 10 | function () external payable { 11 | require(isDepositable()); 12 | } 13 | 14 | function enableDeposits() external isInitialized { 15 | setDepositable(true); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppStubConditionalRecovery.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AragonApp.sol"; 4 | import "../../../common/DepositableStorage.sol"; 5 | 6 | 7 | contract AppStubConditionalRecovery is AragonApp, DepositableStorage { 8 | function initialize() onlyInit public { 9 | initialized(); 10 | setDepositable(true); 11 | } 12 | 13 | function allowRecoverability(address token) public view returns (bool) { 14 | // Doesn't allow to recover ether 15 | return token != address(0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/forwarding/IForwarderFee.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | /** 9 | * @title Forwarder fee interface 10 | * @dev Interface for declaring any fee requirements for the `forward()` function. 11 | */ 12 | interface IForwarderFee { 13 | /** 14 | * @dev Provide details about the required fee token and amount 15 | * @return Fee token and fee amount. If ETH, returns EtherTokenConstant for the `feeToken`. 16 | */ 17 | function forwardFee() external view returns (address feeToken, uint256 feeAmount); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/mocks/evmscript/EVMScriptExecutorNoReturnMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | import "../../../evmscript/executors/BaseEVMScriptExecutor.sol"; 5 | 6 | contract EVMScriptExecutorNoReturnMock is BaseEVMScriptExecutor { 7 | bytes32 internal constant EXECUTOR_TYPE = keccak256("NO_RETURN_SCRIPT"); 8 | 9 | function execScript(bytes, bytes, address[]) external isInitialized returns (bytes) { 10 | assembly { 11 | stop 12 | } 13 | } 14 | 15 | function executorType() external pure returns (bytes32) { 16 | return EXECUTOR_TYPE; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppStorageMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AppStorage.sol"; 4 | 5 | 6 | contract AppStorageMock is AppStorage { 7 | function setKernelExt(IKernel _kernel) public { 8 | setKernel(_kernel); 9 | } 10 | 11 | function setAppIdExt(bytes32 _appId) public { 12 | setAppId(_appId); 13 | } 14 | 15 | function getKernelPosition() public pure returns (bytes32) { 16 | return KERNEL_POSITION; 17 | } 18 | 19 | function getAppIdPosition() public pure returns (bytes32) { 20 | return APP_ID_POSITION; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/lib/math/Math.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title Math 5 | * @dev Assorted math operations 6 | */ 7 | 8 | library Math { 9 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 10 | return a >= b ? a : b; 11 | } 12 | 13 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 14 | return a < b ? a : b; 15 | } 16 | 17 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 18 | return a >= b ? a : b; 19 | } 20 | 21 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 22 | return a < b ? a : b; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/test/mocks/evmscript/EVMScriptExecutorRevertMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | import "../../../evmscript/executors/BaseEVMScriptExecutor.sol"; 5 | 6 | contract EVMScriptExecutorRevertMock is BaseEVMScriptExecutor { 7 | string public constant ERROR_MOCK_REVERT = "MOCK_REVERT"; 8 | bytes32 internal constant EXECUTOR_TYPE = keccak256("MOCK_REVERT_SCRIPT"); 9 | 10 | function execScript(bytes, bytes, address[]) external isInitialized returns (bytes) { 11 | revert(ERROR_MOCK_REVERT); 12 | } 13 | 14 | function executorType() external pure returns (bytes32) { 15 | return EXECUTOR_TYPE; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/common/DepositableStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./UnstructuredStorage.sol"; 4 | 5 | 6 | contract DepositableStorage { 7 | using UnstructuredStorage for bytes32; 8 | 9 | // keccak256("aragonOS.depositableStorage.depositable") 10 | bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea; 11 | 12 | function isDepositable() public view returns (bool) { 13 | return DEPOSITABLE_POSITION.getStorageBool(); 14 | } 15 | 16 | function setDepositable(bool _depositable) internal { 17 | DEPOSITABLE_POSITION.setStorageBool(_depositable); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | For more information about Aragon-related security and contact information, please visit our [security policy page](https://wiki.aragon.org/association/security/). 4 | 5 | ## Status 6 | 7 | aragonOS has passed a number of [independent professional security audits](https://wiki.aragon.org/association/security/). Ongoing, rolling reviews are also conducted from time to time as new changes and releases are frozen. 8 | 9 | There is an ongoing [bug bounty program](https://wiki.aragon.org/dev/bug_bounty/) with a bounty pool of $250,000 USD for rewarding hackers who find new security vulnerabilities. [More information](https://wiki.aragon.org/dev/bug_bounty/). 10 | -------------------------------------------------------------------------------- /contracts/lib/misc/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public lastCompletedMigration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | function Migrations() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) restricted public { 18 | lastCompletedMigration = completed; 19 | } 20 | 21 | function upgrade(address newAddress) restricted public { 22 | Migrations upgraded = Migrations(newAddress); 23 | upgraded.setCompleted(lastCompletedMigration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/test/mocks/evmscript/ExecutionTarget.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | contract ExecutionTarget { 5 | string public constant ERROR_EXECUTION_TARGET = "EXECUTION_TARGET_REVERTED"; 6 | uint public counter; 7 | 8 | function execute() public { 9 | counter += 1; 10 | emit Executed(counter); 11 | } 12 | 13 | function failExecute(bool errorWithData) public pure { 14 | if (errorWithData) { 15 | revert(ERROR_EXECUTION_TARGET); 16 | } else { 17 | revert(); 18 | } 19 | } 20 | 21 | function setCounter(uint x) public { 22 | counter = x; 23 | } 24 | 25 | event Executed(uint x); 26 | } 27 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/disputable/AgreementMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../apps/disputable/IAgreement.sol"; 4 | 5 | 6 | contract AgreementMock is IAgreement { 7 | function newAction(uint256, bytes, address) external returns (uint256) { 8 | return 0; 9 | } 10 | 11 | function closeAction(uint256) external { 12 | // do nothing 13 | } 14 | 15 | function challengeAction(uint256, uint256, bool, bytes) external { 16 | // do nothing 17 | } 18 | 19 | function settleAction(uint256) external { 20 | // do nothing 21 | } 22 | 23 | function disputeAction(uint256, bool) external { 24 | // do nothing 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/contracts/common/uint256_helpers.js: -------------------------------------------------------------------------------- 1 | const { bn } = require('@aragon/contract-helpers-test') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | contract('Uint256 Helpers test', () => { 5 | let uint256Mock 6 | 7 | before(async () => { 8 | uint256Mock = await artifacts.require('Uint256Mock').new() 9 | }) 10 | 11 | it('converts from uint256 to uint64', async () => { 12 | const a = 1234 13 | assert.equal((await uint256Mock.convert(a)).toString(), a, 'values should match') 14 | }) 15 | 16 | it('fails converting from uint256 to uint64 if too big', async () => { 17 | const a = bn(2).pow(bn(64)) 18 | await assertRevert(uint256Mock.convert(a)) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /contracts/lib/standards/ERC165.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | contract ERC165 { 9 | // Includes supportsInterface method: 10 | bytes4 internal constant ERC165_INTERFACE_ID = bytes4(0x01ffc9a7); 11 | 12 | /** 13 | * @dev Query if a contract implements a certain interface 14 | * @param _interfaceId The interface identifier being queried, as specified in ERC-165 15 | * @return True if the contract implements the requested interface and if its not 0xffffffff, false otherwise 16 | */ 17 | function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { 18 | return _interfaceId == ERC165_INTERFACE_ID; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppStubDepositable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AragonApp.sol"; 4 | import "../../../apps/UnsafeAragonApp.sol"; 5 | import "../../../common/DepositableStorage.sol"; 6 | 7 | 8 | contract AppStubDepositable is AragonApp, DepositableStorage { 9 | function () external payable { 10 | require(isDepositable()); 11 | } 12 | 13 | function initialize() onlyInit public { 14 | initialized(); 15 | } 16 | 17 | function enableDeposits() external { 18 | setDepositable(true); 19 | } 20 | } 21 | 22 | contract UnsafeAppStubDepositable is AppStubDepositable, UnsafeAragonApp { 23 | constructor(IKernel _kernel) public { 24 | setKernel(_kernel); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/DepositableDelegateProxyMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/DepositableDelegateProxy.sol"; 4 | 5 | 6 | contract DepositableDelegateProxyMock is DepositableDelegateProxy { 7 | address private implementationMock; 8 | 9 | function enableDepositsOnMock() external { 10 | setDepositable(true); 11 | } 12 | 13 | function setImplementationOnMock(address _implementationMock) external { 14 | implementationMock = _implementationMock; 15 | } 16 | 17 | function implementation() public view returns (address) { 18 | return implementationMock; 19 | } 20 | 21 | function proxyType() public pure returns (uint256 proxyTypeId) { 22 | return UPGRADEABLE; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/test/mocks/evmscript/EVMScriptExecutorMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | import "../../../evmscript/executors/BaseEVMScriptExecutor.sol"; 5 | 6 | contract EVMScriptExecutorMock is BaseEVMScriptExecutor { 7 | bytes32 internal constant EXECUTOR_TYPE = keccak256("MOCK_SCRIPT"); 8 | 9 | function execScript(bytes _script, bytes, address[]) external isInitialized returns (bytes) { 10 | // Return full input script if it's more than just the spec ID, otherwise return an empty 11 | // bytes array 12 | if (_script.length > SCRIPT_START_LOCATION) { 13 | return _script; 14 | } 15 | } 16 | 17 | function executorType() external pure returns (bytes32) { 18 | return EXECUTOR_TYPE; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/common/IsContract.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | contract IsContract { 9 | /* 10 | * NOTE: this should NEVER be used for authentication 11 | * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). 12 | * 13 | * This is only intended to be used as a sanity check that an address is actually a contract, 14 | * RATHER THAN an address not being a contract. 15 | */ 16 | function isContract(address _target) internal view returns (bool) { 17 | if (_target == address(0)) { 18 | return false; 19 | } 20 | 21 | uint256 size; 22 | assembly { size := extcodesize(_target) } 23 | return size > 0; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/common/Petrifiable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./Initializable.sol"; 8 | 9 | 10 | contract Petrifiable is Initializable { 11 | // Use block UINT256_MAX (which should be never) as the initializable date 12 | uint256 internal constant PETRIFIED_BLOCK = uint256(-1); 13 | 14 | function isPetrified() public view returns (bool) { 15 | return getInitializationBlock() == PETRIFIED_BLOCK; 16 | } 17 | 18 | /** 19 | * @dev Function to be called by top level contract to prevent being initialized. 20 | * Useful for freezing base contracts when they're used behind proxies. 21 | */ 22 | function petrify() internal onlyInit { 23 | initializedAt(PETRIFIED_BLOCK); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/kernel/IKernel.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../acl/IACL.sol"; 8 | import "../common/IVaultRecoverable.sol"; 9 | 10 | 11 | interface IKernelEvents { 12 | event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app); 13 | } 14 | 15 | 16 | // This should be an interface, but interfaces can't inherit yet :( 17 | contract IKernel is IKernelEvents, IVaultRecoverable { 18 | function acl() public view returns (IACL); 19 | function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool); 20 | 21 | function setApp(bytes32 namespace, bytes32 appId, address app) public; 22 | function getApp(bytes32 namespace, bytes32 appId) public view returns (address); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/math/SafeMath8Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../lib/math/SafeMath8.sol"; 4 | 5 | 6 | contract SafeMath8Mock { 7 | using SafeMath8 for uint8; 8 | 9 | function mulExt(uint8 _a, uint8 _b) public pure returns (uint8) { 10 | return _a.mul(_b); 11 | } 12 | 13 | function divExt(uint8 _a, uint8 _b) public pure returns (uint8) { 14 | return _a.div(_b); 15 | } 16 | 17 | function subExt(uint8 _a, uint8 _b) public pure returns (uint8) { 18 | return _a.sub(_b); 19 | } 20 | 21 | function addExt(uint8 _a, uint8 _b) public pure returns (uint8) { 22 | return _a.add(_b); 23 | } 24 | 25 | function modExt(uint8 _a, uint8 _b) public pure returns (uint8) { 26 | return _a.mod(_b); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/math/SafeMath64Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../lib/math/SafeMath64.sol"; 4 | 5 | 6 | contract SafeMath64Mock { 7 | using SafeMath64 for uint64; 8 | 9 | function mulExt(uint64 _a, uint64 _b) public pure returns (uint64) { 10 | return _a.mul(_b); 11 | } 12 | 13 | function divExt(uint64 _a, uint64 _b) public pure returns (uint64) { 14 | return _a.div(_b); 15 | } 16 | 17 | function subExt(uint64 _a, uint64 _b) public pure returns (uint64) { 18 | return _a.sub(_b); 19 | } 20 | 21 | function addExt(uint64 _a, uint64 _b) public pure returns (uint64) { 22 | return _a.add(_b); 23 | } 24 | 25 | function modExt(uint64 _a, uint64 _b) public pure returns (uint64) { 26 | return _a.mod(_b); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci_contracts.yml: -------------------------------------------------------------------------------- 1 | name: contracts 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | CI: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Install node 11 | uses: actions/setup-node@v1 12 | with: 13 | node-version: 12 14 | - name: yarn install --frozen-lockfile 15 | run: yarn install 16 | - name: lint 17 | run: yarn lint 18 | - name: test 19 | run: yarn test 20 | - name: coverage 21 | run: yarn coverage 22 | - name: codecov 23 | uses: codecov/codecov-action@v1 24 | with: 25 | flags: unittests 26 | fail_ci_if_error: true 27 | - name: test:gas 28 | continue-on-error: true 29 | run: yarn test:gas:ci 30 | env: 31 | CI: true 32 | -------------------------------------------------------------------------------- /contracts/forwarding/IForwarderWithContext.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IAbstractForwarder.sol"; 8 | 9 | 10 | /** 11 | * @title Forwarder interface requiring context information 12 | * @dev This forwarder interface allows for additional context to be attached to the action by the sender. 13 | */ 14 | contract IForwarderWithContext is IAbstractForwarder { 15 | /** 16 | * @dev Forward an EVM script with an attached context 17 | */ 18 | function forward(bytes evmScript, bytes context) external; 19 | 20 | /** 21 | * @dev Tell the forwarder type 22 | * @return Always 2 (ForwarderType.WITH_CONTEXT) 23 | */ 24 | function forwarderType() external pure returns (ForwarderType) { 25 | return ForwarderType.WITH_CONTEXT; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/forwarding/IForwarder.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IAbstractForwarder.sol"; 8 | 9 | 10 | /** 11 | * @title Forwarder interface 12 | * @dev This is the basic forwarder interface, that only supports forwarding an EVM script. 13 | * It does not support forwarding additional context or receiving ETH; other interfaces are available to support those. 14 | */ 15 | contract IForwarder is IAbstractForwarder { 16 | /** 17 | * @dev Forward an EVM script 18 | */ 19 | function forward(bytes evmScript) external; 20 | 21 | /** 22 | * @dev Tell the forwarder type 23 | * @return Always 1 (ForwarderType.NO_CONTEXT) 24 | */ 25 | function forwarderType() external pure returns (ForwarderType) { 26 | return ForwarderType.NO_CONTEXT; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test/helpers/ThrowProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Assert.sol"; 4 | 5 | // Based on Simon de la Rouviere method: http://truffleframework.com/tutorials/testing-for-throws-in-solidity-tests 6 | 7 | 8 | // Proxy contract for testing throws 9 | contract ThrowProxy { 10 | address public target; 11 | bytes data; 12 | 13 | constructor(address _target) public { 14 | target = _target; 15 | } 16 | 17 | //prime the data using the fallback function. 18 | function() public { 19 | data = msg.data; 20 | } 21 | 22 | function assertThrows(string _msg) public { 23 | Assert.isFalse(execute(), _msg); 24 | } 25 | 26 | function assertItDoesntThrow(string _msg) public { 27 | Assert.isTrue(execute(), _msg); 28 | } 29 | 30 | function execute() public returns (bool) { 31 | return target.call(data); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/test/mocks/kernel/KernelConstantsMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../kernel/Kernel.sol"; 4 | 5 | 6 | contract KernelConstantsMock is Kernel { 7 | constructor() public Kernel(false) { } 8 | 9 | function getKernelCoreNamespace() external pure returns (bytes32) { return KERNEL_CORE_NAMESPACE; } 10 | function getKernelAppBasesNamespace() external pure returns (bytes32) { return KERNEL_APP_BASES_NAMESPACE; } 11 | function getKernelAppAddrNamespace() external pure returns (bytes32) { return KERNEL_APP_ADDR_NAMESPACE; } 12 | function getKernelAppId() external pure returns (bytes32) { return KERNEL_CORE_APP_ID; } 13 | function getDefaultACLAppId() external pure returns (bytes32) { return KERNEL_DEFAULT_ACL_APP_ID; } 14 | function getDefaultVaultAppId() external pure returns (bytes32) { return KERNEL_DEFAULT_VAULT_APP_ID; } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/TimeHelpersMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/TimeHelpers.sol"; 4 | 5 | 6 | contract TimeHelpersMock is TimeHelpers { 7 | function getBlockNumberDirect() public view returns (uint256) { 8 | return block.number; 9 | } 10 | 11 | function getBlockNumberExt() public view returns (uint256) { 12 | return getBlockNumber(); 13 | } 14 | 15 | function getBlockNumber64Ext() public view returns (uint64) { 16 | return getBlockNumber64(); 17 | } 18 | 19 | function getTimestampDirect() public view returns (uint256) { 20 | return now; 21 | } 22 | 23 | function getTimestampExt() public view returns (uint256) { 24 | return getTimestamp(); 25 | } 26 | 27 | function getTimestamp64Ext() public view returns (uint64) { 28 | return getTimestamp64(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/evmscript/IEVMScriptRegistry.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IEVMScriptExecutor.sol"; 8 | 9 | 10 | contract EVMScriptRegistryConstants { 11 | /* Hardcoded constants to save gas 12 | bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg"); 13 | */ 14 | bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61; 15 | } 16 | 17 | 18 | interface IEVMScriptRegistry { 19 | function addScriptExecutor(IEVMScriptExecutor executor) external returns (uint id); 20 | function disableScriptExecutor(uint256 executorId) external; 21 | 22 | // TODO: this should be external 23 | // See https://github.com/ethereum/solidity/issues/4832 24 | function getScriptExecutor(bytes script) public view returns (IEVMScriptExecutor); 25 | } 26 | -------------------------------------------------------------------------------- /test/helpers/errors.js: -------------------------------------------------------------------------------- 1 | const { makeErrorMappingProxy } = require('@aragon/contract-helpers-test') 2 | 3 | module.exports = makeErrorMappingProxy({ 4 | // aragonOS errors 5 | APP_AUTH_FAILED: 'APP_AUTH_FAILED', 6 | INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', 7 | INIT_NOT_INITIALIZED: 'INIT_NOT_INITIALIZED', 8 | RECOVER_DISALLOWED: 'RECOVER_DISALLOWED', 9 | EVMCALLS_CALL_REVERTED: 'EVMCALLS_CALL_REVERTED', 10 | EVMCALLS_INVALID_LENGTH: 'EVMCALLS_INVALID_LENGTH', 11 | EVMREG_EXECUTOR_ENABLED: 'EVMREG_EXECUTOR_ENABLED', 12 | EVMREG_EXECUTOR_DISABLED: 'EVMREG_EXECUTOR_DISABLED', 13 | EVMCALLS_BLACKLISTED_CALL: 'EVMCALLS_BLACKLISTED_CALL', 14 | EVMREG_INEXISTENT_EXECUTOR: 'EVMREG_INEXISTENT_EXECUTOR', 15 | EVMRUN_EXECUTOR_UNAVAILABLE: 'EVMRUN_EXECUTOR_UNAVAILABLE', 16 | EVMREG_SCRIPT_LENGTH_TOO_SHORT: 'EVMREG_SCRIPT_LENGTH_TOO_SHORT', 17 | EVMRUN_EXECUTOR_INVALID_RETURN: 'EVMRUN_EXECUTOR_INVALID_RETURN', 18 | }) 19 | -------------------------------------------------------------------------------- /contracts/apps/UnsafeAragonApp.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../common/UnstructuredStorage.sol"; 8 | import "./AragonApp.sol"; 9 | 10 | 11 | // Using UnsafeAragonApp means you'll be playing with 🔥. 12 | // A number of safe defaults are provided with AragonApp, to help you avoid dangerous situations 13 | // and mistakes with how your contract's developed as well as how it's deployed. 14 | // UnsafeAragonApp turns off these safety features to give you greater control over your contract. 15 | // In particular, it allows you to: 16 | // - Use deployed base contracts as apps directly, without a proxy 17 | contract UnsafeAragonApp is AragonApp { 18 | using UnstructuredStorage for bytes32; 19 | 20 | constructor() public { 21 | // Removes auto petrifying; simulates a delete at INITIALIZATION_BLOCK_POSITION 22 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(0); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/forwarding/IForwarderPayable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IForwarder.sol"; 8 | import "./IForwarderFee.sol"; 9 | 10 | 11 | /** 12 | * @title Payable forwarder interface 13 | * @dev This is the basic forwarder interface, that only supports forwarding an EVM script. 14 | * Unlike `IForwarder`, this interface allows `forward()` to receive ETH and therefore includes the IForwarderFee interface. 15 | * It is **RECOMMENDED** that only apps requiring a payable `forward()` use this interface. 16 | */ 17 | contract IForwarderPayable is IAbstractForwarder, IForwarderFee { 18 | /** 19 | * @dev Forward an EVM script 20 | */ 21 | function forward(bytes evmScript) external payable; 22 | 23 | /** 24 | * @dev Tell the forwarder type 25 | * @return Always 1 (ForwarderType.NO_CONTEXT) 26 | */ 27 | function forwarderType() external pure returns (ForwarderType) { 28 | return ForwarderType.NO_CONTEXT; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/test/tests/TestDelegateProxySelfDestruct.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../helpers/Assert.sol"; 4 | 5 | import "../../common/DelegateProxy.sol"; 6 | import "../../evmscript/ScriptHelpers.sol"; 7 | 8 | 9 | contract Target { 10 | function die() public { selfdestruct(0); } 11 | } 12 | 13 | contract TestDelegateProxySelfDestruct is DelegateProxy { 14 | using ScriptHelpers for *; 15 | 16 | Target target; 17 | 18 | // Mock ERCProxy implementation 19 | function implementation() public view returns (address) { 20 | return this; 21 | } 22 | 23 | function proxyType() public pure returns (uint256) { 24 | return FORWARDING; 25 | } 26 | 27 | function beforeAll() public { 28 | target = new Target(); 29 | } 30 | 31 | function testDieIfMinReturn0() public { 32 | Assert.isTrue(true, ''); // Make at least one assertion to satisfy the runner 33 | 34 | delegatedFwd(target, target.die.selector.toBytes()); 35 | Assert.fail('should be dead'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/common/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../common/UnstructuredStorage.sol"; 8 | 9 | 10 | contract ReentrancyGuard { 11 | using UnstructuredStorage for bytes32; 12 | 13 | /* Hardcoded constants to save gas 14 | bytes32 internal constant REENTRANCY_MUTEX_POSITION = keccak256("aragonOS.reentrancyGuard.mutex"); 15 | */ 16 | bytes32 private constant REENTRANCY_MUTEX_POSITION = 0xe855346402235fdd185c890e68d2c4ecad599b88587635ee285bce2fda58dacb; 17 | 18 | string private constant ERROR_REENTRANT = "REENTRANCY_REENTRANT_CALL"; 19 | 20 | modifier nonReentrant() { 21 | // Ensure mutex is unlocked 22 | require(!REENTRANCY_MUTEX_POSITION.getStorageBool(), ERROR_REENTRANT); 23 | 24 | // Lock mutex before function call 25 | REENTRANCY_MUTEX_POSITION.setStorageBool(true); 26 | 27 | // Perform function call 28 | _; 29 | 30 | // Unlock mutex after function call 31 | REENTRANCY_MUTEX_POSITION.setStorageBool(false); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/forwarding/IForwarderWithContextPayable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IAbstractForwarder.sol"; 8 | import "./IForwarderFee.sol"; 9 | 10 | 11 | /** 12 | * @title Payable forwarder interface requiring context information 13 | * @dev This forwarder interface allows for additional context to be attached to the action by the sender. 14 | * Unlike `IForwarderWithContext`, this interface allows `forward()` to receive ETH and therefore includes the IForwarderFee interface. 15 | * It is **RECOMMENDED** that only apps requiring a payable `forward()` use this interface. 16 | */ 17 | contract IForwarderWithContextPayable is IAbstractForwarder, IForwarderFee { 18 | /** 19 | * @dev Forward an EVM script with an attached context 20 | */ 21 | function forward(bytes evmScript, bytes context) external payable; 22 | 23 | /** 24 | * @dev Tell the forwarder type 25 | * @return Always 2 (ForwarderType.WITH_CONTEXT) 26 | */ 27 | function forwarderType() external pure returns (ForwarderType) { 28 | return ForwarderType.WITH_CONTEXT; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/lib/token/ERC20.sol: -------------------------------------------------------------------------------- 1 | // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol 2 | 3 | pragma solidity ^0.4.24; 4 | 5 | 6 | /** 7 | * @title ERC20 interface 8 | * @dev see https://github.com/ethereum/EIPs/issues/20 9 | */ 10 | contract ERC20 { 11 | function totalSupply() public view returns (uint256); 12 | 13 | function balanceOf(address _who) public view returns (uint256); 14 | 15 | function allowance(address _owner, address _spender) 16 | public view returns (uint256); 17 | 18 | function transfer(address _to, uint256 _value) public returns (bool); 19 | 20 | function approve(address _spender, uint256 _value) 21 | public returns (bool); 22 | 23 | function transferFrom(address _from, address _to, uint256 _value) 24 | public returns (bool); 25 | 26 | event Transfer( 27 | address indexed from, 28 | address indexed to, 29 | uint256 value 30 | ); 31 | 32 | event Approval( 33 | address indexed owner, 34 | address indexed spender, 35 | uint256 value 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /contracts/apps/AppProxyUpgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./AppProxyBase.sol"; 4 | 5 | 6 | contract AppProxyUpgradeable is AppProxyBase { 7 | /** 8 | * @dev Initialize AppProxyUpgradeable (makes it an upgradeable Aragon app) 9 | * @param _kernel Reference to organization kernel for the app 10 | * @param _appId Identifier for app 11 | * @param _initializePayload Payload for call to be made after setup to initialize 12 | */ 13 | constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) 14 | AppProxyBase(_kernel, _appId, _initializePayload) 15 | public // solium-disable-line visibility-first 16 | { 17 | // solium-disable-previous-line no-empty-blocks 18 | } 19 | 20 | /** 21 | * @dev ERC897, the address the proxy would delegate calls to 22 | */ 23 | function implementation() public view returns (address) { 24 | return getAppBase(appId()); 25 | } 26 | 27 | /** 28 | * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy 29 | */ 30 | function proxyType() public pure returns (uint256 proxyTypeId) { 31 | return UPGRADEABLE; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppStub.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AragonApp.sol"; 4 | import "../../../apps/UnsafeAragonApp.sol"; 5 | import "../../../kernel/IKernel.sol"; 6 | 7 | 8 | contract AppStubStorage { 9 | uint a; 10 | string public stringTest; 11 | } 12 | 13 | contract AppStub is AragonApp, AppStubStorage { 14 | bytes32 public constant ROLE = keccak256("ROLE"); 15 | 16 | function initialize() onlyInit public { 17 | initialized(); 18 | stringTest = "hola"; 19 | } 20 | 21 | function requiresInitialization() isInitialized public constant returns (bool) { 22 | return true; 23 | } 24 | 25 | function setValue(uint i) auth(ROLE) public { 26 | a = i; 27 | } 28 | 29 | function setValueParam(uint i) authP(ROLE, arr(i)) public { 30 | a = i; 31 | } 32 | 33 | function getValue() public constant returns (uint) { 34 | return a; 35 | } 36 | } 37 | 38 | contract AppStub2 is AragonApp, AppStubStorage { 39 | function getValue() public constant returns (uint) { 40 | return a * 2; 41 | } 42 | } 43 | 44 | contract UnsafeAppStub is AppStub, UnsafeAragonApp { 45 | constructor(IKernel _kernel) public { 46 | setKernel(_kernel); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/common/DelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../common/IsContract.sol"; 4 | import "../lib/misc/ERCProxy.sol"; 5 | 6 | 7 | contract DelegateProxy is ERCProxy, IsContract { 8 | uint256 internal constant FWD_GAS_LIMIT = 10000; 9 | 10 | /** 11 | * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) 12 | * @param _dst Destination address to perform the delegatecall 13 | * @param _calldata Calldata for the delegatecall 14 | */ 15 | function delegatedFwd(address _dst, bytes _calldata) internal { 16 | require(isContract(_dst)); 17 | uint256 fwdGasLimit = FWD_GAS_LIMIT; 18 | 19 | assembly { 20 | let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) 21 | let size := returndatasize 22 | let ptr := mload(0x40) 23 | returndatacopy(ptr, 0, size) 24 | 25 | // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. 26 | // if the call returned error data, forward it 27 | switch result case 0 { revert(ptr, size) } 28 | default { return(ptr, size) } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/contracts/common/time_helpers.js: -------------------------------------------------------------------------------- 1 | const { bn } = require('@aragon/contract-helpers-test') 2 | 3 | contract('TimeHelpers', () => { 4 | let timeHelpersMock 5 | 6 | before(async () => { 7 | timeHelpersMock = await artifacts.require('TimeHelpersMock').new() 8 | }) 9 | 10 | it('checks block number', async () => { 11 | assert.equal((await timeHelpersMock.getBlockNumberExt()).toString(), (await timeHelpersMock.getBlockNumber64Ext()).toString(), "block numbers should match") 12 | assert.equal((await timeHelpersMock.getBlockNumberExt()).toString(), (await timeHelpersMock.getBlockNumberDirect()).toString(), "block number should match with real one") 13 | }) 14 | 15 | it('checks time stamp', async () => { 16 | const timestamp = await timeHelpersMock.getTimestampExt() 17 | const timestamp64 = await timeHelpersMock.getTimestamp64Ext() 18 | const timestampReal = await timeHelpersMock.getTimestampDirect() 19 | 20 | const timestamp64Diff = timestamp64.sub(timestamp) 21 | assert.isTrue(timestamp64Diff.lte(bn(1)), 'time stamps should match (or be very close to)') 22 | 23 | const timestampRealDiff = timestampReal.sub(timestamp) 24 | assert.isTrue(timestampRealDiff.lte(bn(1)), 'time stamp should match with real one (or be very close to)') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": ["security"], 4 | "rules": { 5 | "security/no-low-level-calls": "off", 6 | "security/no-inline-assembly": "off", 7 | "error-reason": "off", 8 | "imports-on-top": "error", 9 | "variable-declarations": "error", 10 | "array-declarations": "error", 11 | "operator-whitespace": "error", 12 | "conditionals-whitespace": "error", 13 | "comma-whitespace": "error", 14 | "semicolon-whitespace": "error", 15 | "function-whitespace": "error", 16 | "lbrace": "error", 17 | "mixedcase": "error", 18 | "camelcase": "error", 19 | "uppercase": "error", 20 | "no-empty-blocks": "error", 21 | "no-unused-vars": "error", 22 | "quotes": "error", 23 | "blank-lines": "error", 24 | "indentation": "error", 25 | "arg-overflow": ["error", 8], 26 | "whitespace": "error", 27 | "deprecated-suicide": "error", 28 | "pragma-on-top": "error", 29 | "function-order": "error", 30 | "emit": "error", 31 | "no-constant": "error", 32 | "value-in-payable": "error", 33 | "max-len": "error", 34 | "visibility-first": "error", 35 | "linebreak-style": "error" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/apps/AppStorage.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IAragonApp.sol"; 8 | import "../common/UnstructuredStorage.sol"; 9 | import "../kernel/IKernel.sol"; 10 | 11 | 12 | contract AppStorage is IAragonApp { 13 | using UnstructuredStorage for bytes32; 14 | 15 | /* Hardcoded constants to save gas 16 | bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel"); 17 | bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId"); 18 | */ 19 | bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b; 20 | bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b; 21 | 22 | function kernel() public view returns (IKernel) { 23 | return IKernel(KERNEL_POSITION.getStorageAddress()); 24 | } 25 | 26 | function appId() public view returns (bytes32) { 27 | return APP_ID_POSITION.getStorageBytes32(); 28 | } 29 | 30 | function setKernel(IKernel _kernel) internal { 31 | KERNEL_POSITION.setStorageAddress(address(_kernel)); 32 | } 33 | 34 | function setAppId(bytes32 _appId) internal { 35 | APP_ID_POSITION.setStorageBytes32(_appId); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/common/UnstructuredStorage.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | library UnstructuredStorage { 9 | function getStorageBool(bytes32 position) internal view returns (bool data) { 10 | assembly { data := sload(position) } 11 | } 12 | 13 | function getStorageAddress(bytes32 position) internal view returns (address data) { 14 | assembly { data := sload(position) } 15 | } 16 | 17 | function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { 18 | assembly { data := sload(position) } 19 | } 20 | 21 | function getStorageUint256(bytes32 position) internal view returns (uint256 data) { 22 | assembly { data := sload(position) } 23 | } 24 | 25 | function setStorageBool(bytes32 position, bool data) internal { 26 | assembly { sstore(position, data) } 27 | } 28 | 29 | function setStorageAddress(bytes32 position, address data) internal { 30 | assembly { sstore(position, data) } 31 | } 32 | 33 | function setStorageBytes32(bytes32 position, bytes32 data) internal { 34 | assembly { sstore(position, data) } 35 | } 36 | 37 | function setStorageUint256(bytes32 position, uint256 data) internal { 38 | assembly { sstore(position, data) } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/apps/disputable/IDisputable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IAgreement.sol"; 8 | import "../../lib/standards/ERC165.sol"; 9 | import "../../lib/token/ERC20.sol"; 10 | 11 | 12 | contract IDisputable is ERC165 { 13 | // Includes setAgreement, onDisputableActionChallenged, onDisputableActionAllowed, 14 | // onDisputableActionRejected, onDisputableActionVoided, getAgreement, canChallenge, and canClose methods: 15 | bytes4 internal constant DISPUTABLE_INTERFACE_ID = bytes4(0xf3d3bb51); 16 | 17 | event AgreementSet(IAgreement indexed agreement); 18 | 19 | function setAgreement(IAgreement _agreement) external; 20 | 21 | function onDisputableActionChallenged(uint256 _disputableActionId, uint256 _challengeId, address _challenger) external; 22 | 23 | function onDisputableActionAllowed(uint256 _disputableActionId) external; 24 | 25 | function onDisputableActionRejected(uint256 _disputableActionId) external; 26 | 27 | function onDisputableActionVoided(uint256 _disputableActionId) external; 28 | 29 | function getAgreement() external view returns (IAgreement); 30 | 31 | function canChallenge(uint256 _disputableActionId) external view returns (bool); 32 | 33 | function canClose(uint256 _disputableActionId) external view returns (bool); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/test/mocks/forwarding/ForwardingMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../forwarding/IAbstractForwarder.sol"; 4 | import "../../../forwarding/IForwarderFee.sol"; 5 | import "../../../forwarding/IForwarder.sol"; 6 | import "../../../forwarding/IForwarderPayable.sol"; 7 | import "../../../forwarding/IForwarderWithContext.sol"; 8 | import "../../../forwarding/IForwarderWithContextPayable.sol"; 9 | 10 | 11 | contract BaseForwarderMock is IAbstractForwarder { 12 | function canForward(address, bytes) external view returns (bool) { 13 | return true; 14 | } 15 | } 16 | 17 | 18 | contract BaseForwarderPayableMock is BaseForwarderMock, IForwarderFee { 19 | function forwardFee() external view returns (address, uint256) { 20 | return (address(0), 0); 21 | } 22 | } 23 | 24 | 25 | contract ForwarderMock is BaseForwarderMock, IForwarder { 26 | function forward(bytes) external { } 27 | } 28 | 29 | 30 | contract ForwarderPayableMock is BaseForwarderPayableMock, IForwarderPayable { 31 | function forward(bytes) external payable { } 32 | } 33 | 34 | 35 | contract ForwarderWithContextMock is BaseForwarderMock, IForwarderWithContext { 36 | function forward(bytes, bytes) external { } 37 | } 38 | 39 | 40 | contract ForwarderWithContextPayableMock is BaseForwarderPayableMock, IForwarderWithContextPayable { 41 | function forward(bytes, bytes) external payable { } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/common/ConversionHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library ConversionHelpers { 5 | string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH"; 6 | 7 | function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) { 8 | // Force cast the uint256[] into a bytes array, by overwriting its length 9 | // Note that the bytes array doesn't need to be initialized as we immediately overwrite it 10 | // with the input and a new length. The input becomes invalid from this point forward. 11 | uint256 byteLength = _input.length * 32; 12 | assembly { 13 | output := _input 14 | mstore(output, byteLength) 15 | } 16 | } 17 | 18 | function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) { 19 | // Force cast the bytes array into a uint256[], by overwriting its length 20 | // Note that the uint256[] doesn't need to be initialized as we immediately overwrite it 21 | // with the input and a new length. The input becomes invalid from this point forward. 22 | uint256 intsLength = _input.length / 32; 23 | require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH); 24 | 25 | assembly { 26 | output := _input 27 | mstore(output, intsLength) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppProxyPinnedStorageMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AppProxyPinned.sol"; 4 | import "../../../kernel/IKernel.sol"; 5 | import "../../../kernel/Kernel.sol"; 6 | 7 | 8 | contract FakeAppConstants { 9 | bytes32 public constant FAKE_APP_ID = keccak256('FAKE_APP_ID'); 10 | } 11 | 12 | contract KernelPinnedStorageMock is Kernel, FakeAppConstants { 13 | constructor(address _fakeApp) Kernel(false) public { 14 | _setApp(KERNEL_APP_BASES_NAMESPACE, FAKE_APP_ID, _fakeApp); 15 | } 16 | } 17 | 18 | 19 | // Testing this contract is a bit of a pain... we can't overload anything to make the contract check 20 | // pass in the constructor, so we're forced to initialize this with a mocked Kernel that already 21 | // sets a contract for the fake app. 22 | contract AppProxyPinnedStorageMock is AppProxyPinned, FakeAppConstants { 23 | constructor(KernelPinnedStorageMock _mockKernel) 24 | AppProxyPinned(IKernel(_mockKernel), FAKE_APP_ID, new bytes(0)) 25 | public // solium-disable-line visibility-first 26 | { 27 | } 28 | 29 | function setPinnedCodeExt(address _pinnedCode) public { 30 | setPinnedCode(_pinnedCode); 31 | } 32 | 33 | function getPinnedCodePosition() public pure returns (bytes32) { 34 | return PINNED_CODE_POSITION; 35 | } 36 | 37 | function pinnedCodeExt() public view returns (address) { 38 | return pinnedCode(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/token/SafeERC20Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../common/SafeERC20.sol"; 4 | import "../../../../lib/token/ERC20.sol"; 5 | 6 | 7 | contract SafeERC20Mock { 8 | using SafeERC20 for ERC20; 9 | event Result(bool result); 10 | 11 | function transfer(ERC20 token, address to, uint256 amount) external returns (bool) { 12 | bool result = token.safeTransfer(to, amount); 13 | emit Result(result); 14 | return result; 15 | } 16 | 17 | function transferFrom(ERC20 token, address from, address to, uint256 amount) external returns (bool) { 18 | bool result = token.safeTransferFrom(from, to, amount); 19 | emit Result(result); 20 | return result; 21 | } 22 | 23 | function approve(ERC20 token, address spender, uint256 amount) external returns (bool) { 24 | bool result = token.safeApprove(spender, amount); 25 | emit Result(result); 26 | return result; 27 | } 28 | 29 | function allowance(ERC20 token, address owner, address spender) external view returns (uint256) { 30 | return token.staticAllowance(owner, spender); 31 | } 32 | 33 | function balanceOf(ERC20 token, address owner) external view returns (uint256) { 34 | return token.staticBalanceOf(owner); 35 | } 36 | 37 | function totalSupply(ERC20 token) external view returns (uint256) { 38 | return token.staticTotalSupply(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/apps/disputable/IAgreement.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../../lib/token/ERC20.sol"; 8 | 9 | 10 | contract IAgreement { 11 | 12 | event ActionSubmitted(uint256 indexed actionId, address indexed disputable); 13 | event ActionClosed(uint256 indexed actionId); 14 | event ActionChallenged(uint256 indexed actionId, uint256 indexed challengeId); 15 | event ActionSettled(uint256 indexed actionId, uint256 indexed challengeId); 16 | event ActionDisputed(uint256 indexed actionId, uint256 indexed challengeId); 17 | event ActionAccepted(uint256 indexed actionId, uint256 indexed challengeId); 18 | event ActionVoided(uint256 indexed actionId, uint256 indexed challengeId); 19 | event ActionRejected(uint256 indexed actionId, uint256 indexed challengeId); 20 | 21 | enum ChallengeState { 22 | Waiting, 23 | Settled, 24 | Disputed, 25 | Rejected, 26 | Accepted, 27 | Voided 28 | } 29 | 30 | function newAction(uint256 _disputableActionId, bytes _context, address _submitter) external returns (uint256); 31 | 32 | function closeAction(uint256 _actionId) external; 33 | 34 | function challengeAction(uint256 _actionId, uint256 _settlementOffer, bool _finishedSubmittingEvidence, bytes _context) external; 35 | 36 | function settleAction(uint256 _actionId) external; 37 | 38 | function disputeAction(uint256 _actionId, bool _finishedSubmittingEvidence) external; 39 | } 40 | -------------------------------------------------------------------------------- /contracts/kernel/KernelConstants.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | contract KernelAppIds { 9 | /* Hardcoded constants to save gas 10 | bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel"); 11 | bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl"); 12 | bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault"); 13 | */ 14 | bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c; 15 | bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a; 16 | bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1; 17 | } 18 | 19 | 20 | contract KernelNamespaceConstants { 21 | /* Hardcoded constants to save gas 22 | bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core"); 23 | bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base"); 24 | bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app"); 25 | */ 26 | bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8; 27 | bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f; 28 | bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb; 29 | } 30 | -------------------------------------------------------------------------------- /contracts/common/TimeHelpers.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./Uint256Helpers.sol"; 8 | 9 | 10 | contract TimeHelpers { 11 | using Uint256Helpers for uint256; 12 | 13 | /** 14 | * @dev Returns the current block number. 15 | * Using a function rather than `block.number` allows us to easily mock the block number in 16 | * tests. 17 | */ 18 | function getBlockNumber() internal view returns (uint256) { 19 | return block.number; 20 | } 21 | 22 | /** 23 | * @dev Returns the current block number, converted to uint64. 24 | * Using a function rather than `block.number` allows us to easily mock the block number in 25 | * tests. 26 | */ 27 | function getBlockNumber64() internal view returns (uint64) { 28 | return getBlockNumber().toUint64(); 29 | } 30 | 31 | /** 32 | * @dev Returns the current timestamp. 33 | * Using a function rather than `block.timestamp` allows us to easily mock it in 34 | * tests. 35 | */ 36 | function getTimestamp() internal view returns (uint256) { 37 | return block.timestamp; // solium-disable-line security/no-block-members 38 | } 39 | 40 | /** 41 | * @dev Returns the current timestamp, converted to uint64. 42 | * Using a function rather than `block.timestamp` allows us to easily mock it in 43 | * tests. 44 | */ 45 | function getTimestamp64() internal view returns (uint64) { 46 | return getTimestamp().toUint64(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aragon/os", 3 | "version": "5.0.0-rc.0", 4 | "author": "Aragon Association ", 5 | "license": "(GPL-3.0-or-later OR MIT)", 6 | "repository": "https://github.com/aragon/aragonOS", 7 | "description": "Core contracts for Aragon", 8 | "contributors": [ 9 | "Jorge Izquierdo " 10 | ], 11 | "files": [ 12 | "/abi", 13 | "/artifacts", 14 | "/contracts" 15 | ], 16 | "scripts": { 17 | "console": "buidler console", 18 | "compile": "buidler compile --force", 19 | "devchain": "buidler node", 20 | "lint": "solium --dir ./contracts", 21 | "test": "buidler test", 22 | "test:gas": "REPORT_GAS=true yarn test --network localhost", 23 | "test:gas:ci": "yarn test:gas && npx codechecks", 24 | "coverage": "buidler coverage --network coverage", 25 | "abi:extract": "buidler-extract --output abi/ --keys abi", 26 | "prepublishOnly": "yarn compile && yarn abi:extract -- --no-compile" 27 | }, 28 | "dependencies": {}, 29 | "devDependencies": { 30 | "@aragon/contract-helpers-test": "^0.1.0", 31 | "@codechecks/client": "^0.1.5", 32 | "@nomiclabs/buidler": "^1.4.3", 33 | "@nomiclabs/buidler-ganache": "^1.3.3", 34 | "@nomiclabs/buidler-truffle5": "^1.3.4", 35 | "@nomiclabs/buidler-web3": "^1.3.4", 36 | "buidler-extract": "^1.0.0", 37 | "buidler-gas-reporter": "^0.1.3", 38 | "chai": "^4.2.0", 39 | "eth-ens-namehash": "^2.0.8", 40 | "ethereumjs-abi": "^0.6.5", 41 | "solidity-coverage": "^0.7.9", 42 | "solium": "^1.2.5", 43 | "web3": "^1.2.11" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/factory/EVMScriptRegistryFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../evmscript/IEVMScriptExecutor.sol"; 4 | import "../evmscript/EVMScriptRegistry.sol"; 5 | 6 | import "../evmscript/executors/CallsScript.sol"; 7 | 8 | import "../kernel/Kernel.sol"; 9 | import "../acl/ACL.sol"; 10 | 11 | 12 | contract EVMScriptRegistryFactory is EVMScriptRegistryConstants { 13 | EVMScriptRegistry public baseReg; 14 | IEVMScriptExecutor public baseCallScript; 15 | 16 | /** 17 | * @notice Create a new EVMScriptRegistryFactory. 18 | */ 19 | constructor() public { 20 | baseReg = new EVMScriptRegistry(); 21 | baseCallScript = IEVMScriptExecutor(new CallsScript()); 22 | } 23 | 24 | /** 25 | * @notice Install a new pinned instance of EVMScriptRegistry on `_dao`. 26 | * @param _dao Kernel 27 | * @return Installed EVMScriptRegistry 28 | */ 29 | function newEVMScriptRegistry(Kernel _dao) public returns (EVMScriptRegistry reg) { 30 | bytes memory initPayload = abi.encodeWithSelector(reg.initialize.selector); 31 | reg = EVMScriptRegistry(_dao.newPinnedAppInstance(EVMSCRIPT_REGISTRY_APP_ID, baseReg, initPayload, true)); 32 | 33 | ACL acl = ACL(_dao.acl()); 34 | 35 | acl.createPermission(this, reg, reg.REGISTRY_ADD_EXECUTOR_ROLE(), this); 36 | 37 | reg.addScriptExecutor(baseCallScript); // spec 1 = CallsScript 38 | 39 | // Clean up the permissions 40 | acl.revokePermission(this, reg, reg.REGISTRY_ADD_EXECUTOR_ROLE()); 41 | acl.removePermissionManager(reg, reg.REGISTRY_ADD_EXECUTOR_ROLE()); 42 | 43 | return reg; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/apps/AppProxyBase.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./AppStorage.sol"; 4 | import "../common/DepositableDelegateProxy.sol"; 5 | import "../kernel/KernelConstants.sol"; 6 | import "../kernel/IKernel.sol"; 7 | 8 | 9 | contract AppProxyBase is AppStorage, DepositableDelegateProxy, KernelNamespaceConstants { 10 | /** 11 | * @dev Initialize AppProxy 12 | * @param _kernel Reference to organization kernel for the app 13 | * @param _appId Identifier for app 14 | * @param _initializePayload Payload for call to be made after setup to initialize 15 | */ 16 | constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public { 17 | setKernel(_kernel); 18 | setAppId(_appId); 19 | 20 | // Implicit check that kernel is actually a Kernel 21 | // The EVM doesn't actually provide a way for us to make sure, but we can force a revert to 22 | // occur if the kernel is set to 0x0 or a non-code address when we try to call a method on 23 | // it. 24 | address appCode = getAppBase(_appId); 25 | 26 | // If initialize payload is provided, it will be executed 27 | if (_initializePayload.length > 0) { 28 | require(isContract(appCode)); 29 | // Cannot make delegatecall as a delegateproxy.delegatedFwd as it 30 | // returns ending execution context and halts contract deployment 31 | require(appCode.delegatecall(_initializePayload)); 32 | } 33 | } 34 | 35 | function getAppBase(bytes32 _appId) internal view returns (address) { 36 | return kernel().getApp(KERNEL_APP_BASES_NAMESPACE, _appId); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/ReentrancyGuardMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../common/ReentrancyGuard.sol"; 4 | import "../../../common/UnstructuredStorage.sol"; 5 | 6 | 7 | contract ReentrantActor { 8 | bool reenterNonReentrant; 9 | 10 | constructor(bool _reenterNonReentrant) public { 11 | reenterNonReentrant = _reenterNonReentrant; 12 | } 13 | 14 | function reenter(ReentrancyGuardMock _mock) public { 15 | // Set the reentrancy target to 0 so we don't infinite loop 16 | ReentrantActor reentrancyTarget = ReentrantActor(0); 17 | 18 | if (reenterNonReentrant) { 19 | _mock.nonReentrantCall(reentrancyTarget); 20 | } else { 21 | _mock.reentrantCall(reentrancyTarget); 22 | } 23 | } 24 | } 25 | 26 | 27 | contract ReentrancyGuardMock is ReentrancyGuard { 28 | using UnstructuredStorage for bytes32; 29 | 30 | uint256 public callCounter; 31 | 32 | function nonReentrantCall(ReentrantActor _target) public nonReentrant { 33 | callCounter++; 34 | if (_target != address(0)) { 35 | _target.reenter(this); 36 | } 37 | } 38 | 39 | function reentrantCall(ReentrantActor _target) public { 40 | callCounter++; 41 | if (_target != address(0)) { 42 | _target.reenter(this); 43 | } 44 | } 45 | 46 | function setReentrancyMutex(bool _mutex) public { 47 | getReentrancyMutexPosition().setStorageBool(_mutex); 48 | } 49 | 50 | function getReentrancyMutexPosition() public pure returns (bytes32) { 51 | return keccak256("aragonOS.reentrancyGuard.mutex"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/evmscript/ScriptHelpers.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | library ScriptHelpers { 9 | function getSpecId(bytes _script) internal pure returns (uint32) { 10 | return uint32At(_script, 0); 11 | } 12 | 13 | function uint256At(bytes _data, uint256 _location) internal pure returns (uint256 result) { 14 | assembly { 15 | result := mload(add(_data, add(0x20, _location))) 16 | } 17 | } 18 | 19 | function addressAt(bytes _data, uint256 _location) internal pure returns (address result) { 20 | uint256 word = uint256At(_data, _location); 21 | 22 | assembly { 23 | result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000), 24 | 0x1000000000000000000000000) 25 | } 26 | } 27 | 28 | function uint32At(bytes _data, uint256 _location) internal pure returns (uint32 result) { 29 | uint256 word = uint256At(_data, _location); 30 | 31 | assembly { 32 | result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000), 33 | 0x100000000000000000000000000000000000000000000000000000000) 34 | } 35 | } 36 | 37 | function locationOf(bytes _data, uint256 _location) internal pure returns (uint256 result) { 38 | assembly { 39 | result := add(_data, add(0x20, _location)) 40 | } 41 | } 42 | 43 | function toBytes(bytes4 _sig) internal pure returns (bytes) { 44 | bytes memory payload = new bytes(4); 45 | assembly { mstore(add(payload, 0x20), _sig) } 46 | return payload; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/kernel/KernelProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./IKernel.sol"; 4 | import "./KernelConstants.sol"; 5 | import "./KernelStorage.sol"; 6 | import "../common/DepositableDelegateProxy.sol"; 7 | import "../common/IsContract.sol"; 8 | 9 | 10 | contract KernelProxy is IKernelEvents, KernelStorage, KernelAppIds, KernelNamespaceConstants, IsContract, DepositableDelegateProxy { 11 | /** 12 | * @dev KernelProxy is a proxy contract to a kernel implementation. The implementation 13 | * can update the reference, which effectively upgrades the contract 14 | * @param _kernelImpl Address of the contract used as implementation for kernel 15 | */ 16 | constructor(IKernel _kernelImpl) public { 17 | require(isContract(address(_kernelImpl))); 18 | apps[KERNEL_CORE_NAMESPACE][KERNEL_CORE_APP_ID] = _kernelImpl; 19 | 20 | // Note that emitting this event is important for verifying that a KernelProxy instance 21 | // was never upgraded to a malicious Kernel logic contract over its lifespan. 22 | // This starts the "chain of trust", that can be followed through later SetApp() events 23 | // emitted during kernel upgrades. 24 | emit SetApp(KERNEL_CORE_NAMESPACE, KERNEL_CORE_APP_ID, _kernelImpl); 25 | } 26 | 27 | /** 28 | * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy 29 | */ 30 | function proxyType() public pure returns (uint256 proxyTypeId) { 31 | return UPGRADEABLE; 32 | } 33 | 34 | /** 35 | * @dev ERC897, the address the proxy would delegate calls to 36 | */ 37 | function implementation() public view returns (address) { 38 | return apps[KERNEL_CORE_NAMESPACE][KERNEL_CORE_APP_ID]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/forwarding/IAbstractForwarder.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | /** 9 | * @title Abstract forwarder interface 10 | * @dev This is the base interface for all forwarders. 11 | * Forwarding allows separately installed applications (smart contracts implementing the forwarding interface) to execute multi-step actions via EVM scripts. 12 | * You should only support the forwarding interface if your "action step" is asynchronous (e.g. requiring a delay period or a voting period). 13 | * Note: you should **NOT** directly inherit from this interface; see one of the other, non-abstract interfaces available. 14 | */ 15 | contract IAbstractForwarder { 16 | enum ForwarderType { 17 | NOT_IMPLEMENTED, 18 | NO_CONTEXT, 19 | WITH_CONTEXT 20 | } 21 | 22 | /** 23 | * @dev Tell whether the proposed forwarding path (an EVM script) from the given sender is allowed. 24 | * However, this is not a strict guarantee of safety: the implemented `forward()` method is 25 | * still allowed to revert even if `canForward()` returns true for the same parameters. 26 | * @return True if the sender's proposed path is allowed 27 | */ 28 | function canForward(address sender, bytes evmScript) external view returns (bool); 29 | 30 | /** 31 | * @dev Tell the forwarder type 32 | * @return Forwarder type 33 | */ 34 | function forwarderType() external pure returns (ForwarderType); 35 | 36 | /** 37 | * @dev Report whether the implementing app is a forwarder 38 | * Required for backwards compatibility with aragonOS 4 39 | * @return Always true 40 | */ 41 | function isForwarder() external pure returns (bool) { 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /contracts/test/mocks/kernel/KernelOverloadMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../kernel/Kernel.sol"; 4 | import "../../../lib/misc/ERCProxy.sol"; 5 | 6 | 7 | /** Ugly hack to work around this issue: 8 | * https://github.com/trufflesuite/truffle/issues/569 9 | * https://github.com/trufflesuite/truffle/issues/737 10 | * 11 | * NOTE: awkwardly, by default we have access to the full version of `newAppInstance()` but only the 12 | * minimized version for `newPinnedAppInstance()` 13 | */ 14 | contract KernelOverloadMock { 15 | Kernel public kernel; 16 | 17 | event NewAppProxy(address proxy); 18 | 19 | constructor(Kernel _kernel) public { 20 | kernel = _kernel; 21 | } 22 | 23 | /* 24 | function newAppInstance(bytes32 _appId, address _appBase) 25 | public 26 | auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId)) 27 | returns (ERCProxy appProxy) 28 | */ 29 | function newAppInstance(bytes32 _appId, address _appBase) 30 | public 31 | returns (ERCProxy appProxy) 32 | { 33 | appProxy = kernel.newAppInstance(_appId, _appBase); 34 | emit NewAppProxy(appProxy); 35 | } 36 | 37 | /* 38 | function newPinnedAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault) 39 | public 40 | auth(APP_MANAGER_ROLE, arr(KERNEL_APP_BASES_NAMESPACE, _appId)) 41 | returns (ERCProxy appProxy) 42 | */ 43 | function newPinnedAppInstance(bytes32 _appId, address _appBase, bytes _initializePayload, bool _setDefault) 44 | public 45 | returns (ERCProxy appProxy) 46 | { 47 | appProxy = kernel.newPinnedAppInstance(_appId, _appBase, _initializePayload, _setDefault); 48 | emit NewAppProxy(appProxy); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/apps/AppProxyPinned.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../common/UnstructuredStorage.sol"; 4 | import "../common/IsContract.sol"; 5 | import "./AppProxyBase.sol"; 6 | 7 | 8 | contract AppProxyPinned is IsContract, AppProxyBase { 9 | using UnstructuredStorage for bytes32; 10 | 11 | // keccak256("aragonOS.appStorage.pinnedCode") 12 | bytes32 internal constant PINNED_CODE_POSITION = 0xdee64df20d65e53d7f51cb6ab6d921a0a6a638a91e942e1d8d02df28e31c038e; 13 | 14 | /** 15 | * @dev Initialize AppProxyPinned (makes it an un-upgradeable Aragon app) 16 | * @param _kernel Reference to organization kernel for the app 17 | * @param _appId Identifier for app 18 | * @param _initializePayload Payload for call to be made after setup to initialize 19 | */ 20 | constructor(IKernel _kernel, bytes32 _appId, bytes _initializePayload) 21 | AppProxyBase(_kernel, _appId, _initializePayload) 22 | public // solium-disable-line visibility-first 23 | { 24 | setPinnedCode(getAppBase(_appId)); 25 | require(isContract(pinnedCode())); 26 | } 27 | 28 | /** 29 | * @dev ERC897, the address the proxy would delegate calls to 30 | */ 31 | function implementation() public view returns (address) { 32 | return pinnedCode(); 33 | } 34 | 35 | /** 36 | * @dev ERC897, whether it is a forwarding (1) or an upgradeable (2) proxy 37 | */ 38 | function proxyType() public pure returns (uint256 proxyTypeId) { 39 | return FORWARDING; 40 | } 41 | 42 | function setPinnedCode(address _pinnedCode) internal { 43 | PINNED_CODE_POSITION.setStorageAddress(_pinnedCode); 44 | } 45 | 46 | function pinnedCode() internal view returns (address) { 47 | return PINNED_CODE_POSITION.getStorageAddress(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/contracts/apps/app_interface.js: -------------------------------------------------------------------------------- 1 | const { getEventArgument } = require('@aragon/contract-helpers-test') 2 | const { getInstalledApp } = require('@aragon/contract-helpers-test/src/aragon-os') 3 | 4 | const ACL = artifacts.require('ACL') 5 | const Kernel = artifacts.require('Kernel') 6 | const DAOFactory = artifacts.require('DAOFactory') 7 | const EVMScriptRegistryFactory = artifacts.require('EVMScriptRegistryFactory') 8 | 9 | const AragonAppMock = artifacts.require('AragonAppMock') 10 | const ERC165Mock = artifacts.require('ERC165Mock') 11 | 12 | const ARAGON_APP_INTERFACE = '0x54053e6c' 13 | const ERC165_INTERFACE = '0x01ffc9a7' 14 | 15 | contract('AragonApp', ([_, owner]) => { 16 | let aragonApp 17 | 18 | before('deploy AragonAppMock', async () => { 19 | aragonApp = await AragonAppMock.new() 20 | }) 21 | 22 | describe('supportsInterface', () => { 23 | it('supports ERC165', async () => { 24 | const erc165 = await ERC165Mock.new() 25 | assert.isTrue(await aragonApp.supportsInterface(ERC165_INTERFACE), 'does not support ERC165') 26 | 27 | assert.equal(await erc165.interfaceID(), ERC165_INTERFACE, 'ERC165 interface ID does not match') 28 | assert.equal(await erc165.ERC165_INTERFACE(), ERC165_INTERFACE, 'ERC165 interface ID does not match') 29 | }) 30 | 31 | it('supports AragonApp interface', async () => { 32 | assert.isTrue(await aragonApp.supportsInterface(ARAGON_APP_INTERFACE), 'does not support AragonApp interface') 33 | 34 | assert.equal(await aragonApp.interfaceID(), ARAGON_APP_INTERFACE, 'AragonApp interface ID does not match') 35 | assert.equal(await aragonApp.ARAGON_APP_INTERFACE(), ARAGON_APP_INTERFACE, 'AragonApp interface ID does not match') 36 | }) 37 | 38 | it('does not support 0xffffffff', async () => { 39 | assert.isFalse(await aragonApp.supportsInterface('0xffffffff'), 'should not support 0xffffffff') 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /contracts/test/tests/TestDelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../helpers/Assert.sol"; 4 | import "../helpers/ThrowProxy.sol"; 5 | 6 | import "../../common/DelegateProxy.sol"; 7 | import "../../evmscript/ScriptHelpers.sol"; 8 | 9 | 10 | contract Target { 11 | function dontReturn() public pure {} 12 | function fail() public pure { revert(); } 13 | } 14 | 15 | 16 | contract TestDelegateProxy is DelegateProxy { 17 | using ScriptHelpers for *; 18 | 19 | Target target; 20 | ThrowProxy throwProxy; 21 | 22 | // Mock ERCProxy implementation 23 | function implementation() public view returns (address) { 24 | return this; 25 | } 26 | 27 | function proxyType() public pure returns (uint256) { 28 | return FORWARDING; 29 | } 30 | 31 | // Tests 32 | function beforeAll() public { 33 | target = new Target(); 34 | } 35 | 36 | function beforeEach() public { 37 | throwProxy = new ThrowProxy(address(this)); 38 | } 39 | 40 | function testFailIfNoContract() public { 41 | TestDelegateProxy(throwProxy).noContract(); 42 | throwProxy.assertThrows("should have reverted if target is not a contract"); 43 | } 44 | 45 | function noContract() public { 46 | delegatedFwd(address(0x1234), target.dontReturn.selector.toBytes()); 47 | } 48 | 49 | function testFailIfReverts() public { 50 | TestDelegateProxy(throwProxy).revertCall(); 51 | throwProxy.assertThrows("should have reverted if call reverted"); 52 | } 53 | 54 | function revertCall() public { 55 | delegatedFwd(target, target.fail.selector.toBytes()); 56 | } 57 | 58 | function testIsContractZero() public { 59 | bool result = isContract(address(0)); 60 | Assert.isFalse(result, "should return false"); 61 | } 62 | 63 | function testIsContractAddress() public { 64 | address nonContract = 0x1234; 65 | bool result = isContract(nonContract); 66 | Assert.isFalse(result, "should return false"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /contracts/common/VaultRecoverable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "../lib/token/ERC20.sol"; 8 | import "./EtherTokenConstant.sol"; 9 | import "./IsContract.sol"; 10 | import "./IVaultRecoverable.sol"; 11 | import "./SafeERC20.sol"; 12 | 13 | 14 | contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract { 15 | using SafeERC20 for ERC20; 16 | 17 | string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED"; 18 | string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT"; 19 | string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED"; 20 | 21 | /** 22 | * @notice Send funds to recovery Vault. This contract should never receive funds, 23 | * but in case it does, this function allows one to recover them. 24 | * @param _token Token balance to be sent to recovery vault. 25 | */ 26 | function transferToVault(address _token) external { 27 | require(allowRecoverability(_token), ERROR_DISALLOWED); 28 | address vault = getRecoveryVault(); 29 | require(isContract(vault), ERROR_VAULT_NOT_CONTRACT); 30 | 31 | uint256 balance; 32 | if (_token == ETH) { 33 | balance = address(this).balance; 34 | vault.transfer(balance); 35 | } else { 36 | ERC20 token = ERC20(_token); 37 | balance = token.staticBalanceOf(this); 38 | require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED); 39 | } 40 | 41 | emit RecoverToVault(vault, _token, balance); 42 | } 43 | 44 | /** 45 | * @dev By default deriving from AragonApp makes it recoverable 46 | * @param token Token address that would be recovered 47 | * @return bool whether the app allows the recovery 48 | */ 49 | function allowRecoverability(address token) public view returns (bool) { 50 | return true; 51 | } 52 | 53 | // Cast non-implemented interface to be public so we can use it internally 54 | function getRecoveryVault() public view returns (address); 55 | } 56 | -------------------------------------------------------------------------------- /contracts/common/Initializable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./TimeHelpers.sol"; 8 | import "./UnstructuredStorage.sol"; 9 | 10 | 11 | contract Initializable is TimeHelpers { 12 | using UnstructuredStorage for bytes32; 13 | 14 | // keccak256("aragonOS.initializable.initializationBlock") 15 | bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; 16 | 17 | string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; 18 | string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; 19 | 20 | modifier onlyInit { 21 | require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); 22 | _; 23 | } 24 | 25 | modifier isInitialized { 26 | require(hasInitialized(), ERROR_NOT_INITIALIZED); 27 | _; 28 | } 29 | 30 | /** 31 | * @return Block number in which the contract was initialized 32 | */ 33 | function getInitializationBlock() public view returns (uint256) { 34 | return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); 35 | } 36 | 37 | /** 38 | * @return Whether the contract has been initialized by the time of the current block 39 | */ 40 | function hasInitialized() public view returns (bool) { 41 | uint256 initializationBlock = getInitializationBlock(); 42 | return initializationBlock != 0 && getBlockNumber() >= initializationBlock; 43 | } 44 | 45 | /** 46 | * @dev Function to be called by top level contract after initialization has finished. 47 | */ 48 | function initialized() internal onlyInit { 49 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); 50 | } 51 | 52 | /** 53 | * @dev Function to be called by top level contract after initialization to enable the contract 54 | * at a future block number rather than immediately. 55 | */ 56 | function initializedAt(uint256 _blockNumber) internal onlyInit { 57 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/contracts/lib/math/lib_safemath8.js: -------------------------------------------------------------------------------- 1 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 2 | 3 | contract('SafeMath8 lib test', () => { 4 | let safeMath8Mock 5 | 6 | before(async () => { 7 | safeMath8Mock = await artifacts.require('SafeMath8Mock').new() 8 | }) 9 | 10 | // multiplication 11 | it('multiplies', async () => { 12 | const a = 16 13 | const b = 15 14 | assert.equal((await safeMath8Mock.mulExt(a, b)).toString(), a * b, 'values should match') 15 | }) 16 | 17 | it('fails if multplication overflows', async () => { 18 | const a = 16 19 | const b = 16 20 | await assertRevert(safeMath8Mock.mulExt(a, b)) 21 | }) 22 | 23 | // division 24 | it('divides', async () => { 25 | const a = 149 26 | const b = 50 27 | assert.equal((await safeMath8Mock.divExt(a, b)).toString(), Math.floor(a / b), 'values should match') 28 | }) 29 | 30 | it('fails dividing by zero', async () => { 31 | const a = 123 32 | const b = 0 33 | await assertRevert(safeMath8Mock.divExt(a, b)) 34 | }) 35 | 36 | // subtraction 37 | it('subtract', async () => { 38 | const a = 123 39 | const b = 122 40 | assert.equal((await safeMath8Mock.subExt(a, b)).toString(), a - b, 'values should match') 41 | }) 42 | 43 | it('fails if subtraction underflows', async () => { 44 | const a = 123 45 | const b = 124 46 | await assertRevert(safeMath8Mock.subExt(a, b)) 47 | }) 48 | 49 | // addition 50 | it('adds', async () => { 51 | const a = 128 52 | const b = 127 53 | assert.equal((await safeMath8Mock.addExt(a, b)).toString(), a + b, 'values should match') 54 | }) 55 | 56 | it('fails if addition overflows', async () => { 57 | const a = 128 58 | const b = 128 59 | await assertRevert(safeMath8Mock.addExt(a, b)) 60 | }) 61 | 62 | // modulo 63 | it('divides modulo', async () => { 64 | const a = 149 65 | const b = 50 66 | assert.equal((await safeMath8Mock.modExt(a, b)).toString(), a % b, 'values should match') 67 | }) 68 | 69 | it('fails modulo dividing by zero', async () => { 70 | const a = 123 71 | const b = 0 72 | await assertRevert(safeMath8Mock.modExt(a, b)) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/AppStubScriptRunner.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../apps/AragonApp.sol"; 4 | 5 | 6 | contract AppStubScriptRunner is AragonApp { 7 | event ReturnedBytes(bytes returnedBytes); 8 | 9 | // Initialization is required to access any of the real executors 10 | function initialize() public { 11 | initialized(); 12 | } 13 | 14 | function runScript(bytes script) public returns (bytes) { 15 | bytes memory returnedBytes = runScript(script, new bytes(0), new address[](0)); 16 | emit ReturnedBytes(returnedBytes); 17 | return returnedBytes; 18 | } 19 | 20 | function runScriptWithBan(bytes script, address[] memory blacklist) public returns (bytes) { 21 | bytes memory returnedBytes = runScript(script, new bytes(0), blacklist); 22 | emit ReturnedBytes(returnedBytes); 23 | return returnedBytes; 24 | } 25 | 26 | function runScriptWithIO(bytes script, bytes input, address[] memory blacklist) public returns (bytes) { 27 | bytes memory returnedBytes = runScript(script, input, blacklist); 28 | emit ReturnedBytes(returnedBytes); 29 | return returnedBytes; 30 | } 31 | 32 | function runScriptWithNewBytesAllocation(bytes script) public returns (bytes) { 33 | bytes memory returnedBytes = runScript(script, new bytes(0), new address[](0)); 34 | bytes memory newBytes = new bytes(64); 35 | 36 | // Fill in new bytes array with some dummy data to let us check it doesn't corrupt the 37 | // script's returned bytes 38 | uint256 first = uint256(keccak256("test")); 39 | uint256 second = uint256(keccak256("mock")); 40 | assembly { 41 | mstore(add(newBytes, 0x20), first) 42 | mstore(add(newBytes, 0x40), second) 43 | } 44 | emit ReturnedBytes(returnedBytes); 45 | return returnedBytes; 46 | } 47 | 48 | /* 49 | function getActionsCount(bytes script) public constant returns (uint256) { 50 | return getScriptActionsCount(script); 51 | } 52 | 53 | function getAction(bytes script, uint256 i) public constant returns (address, bytes) { 54 | return getScriptAction(script, i); 55 | } 56 | */ 57 | } 58 | -------------------------------------------------------------------------------- /contracts/common/DepositableDelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./DelegateProxy.sol"; 4 | import "./DepositableStorage.sol"; 5 | 6 | 7 | contract DepositableDelegateProxy is DepositableStorage, DelegateProxy { 8 | event ProxyDeposit(address sender, uint256 value); 9 | 10 | function () external payable { 11 | uint256 forwardGasThreshold = FWD_GAS_LIMIT; 12 | bytes32 isDepositablePosition = DEPOSITABLE_POSITION; 13 | 14 | // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity: 15 | // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20 16 | assembly { 17 | // Continue only if the gas left is lower than the threshold for forwarding to the implementation code, 18 | // otherwise continue outside of the assembly block. 19 | if lt(gas, forwardGasThreshold) { 20 | // Only accept the deposit and emit an event if all of the following are true: 21 | // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0 22 | if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) { 23 | // Equivalent Solidity code for emitting the event: 24 | // emit ProxyDeposit(msg.sender, msg.value); 25 | 26 | let logData := mload(0x40) // free memory pointer 27 | mstore(logData, caller) // add 'msg.sender' to the log data (first event param) 28 | mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param) 29 | 30 | // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1 31 | log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1) 32 | 33 | stop() // Stop. Exits execution context 34 | } 35 | 36 | // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender) 37 | revert(0, 0) 38 | } 39 | } 40 | 41 | address target = implementation(); 42 | delegatedFwd(target, msg.data); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/contracts/lib/math/lib_safemath64.js: -------------------------------------------------------------------------------- 1 | const { bn } = require('@aragon/contract-helpers-test') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | contract('SafeMath64 lib test', () => { 5 | let safeMath64Mock 6 | 7 | before(async () => { 8 | safeMath64Mock = await artifacts.require('SafeMath64Mock').new() 9 | }) 10 | 11 | // multiplication 12 | it('multiplies', async () => { 13 | const a = 1234 14 | const b = 32746 15 | assert.equal((await safeMath64Mock.mulExt(a, b)).toString(), a * b, 'values should match') 16 | }) 17 | 18 | it('fails if multplication overflows', async () => { 19 | const a = bn(2).pow(bn(63)) 20 | const b = 2 21 | await assertRevert(safeMath64Mock.mulExt(a, b)) 22 | }) 23 | 24 | // division 25 | it('divides', async () => { 26 | const a = 32746 27 | const b = 1234 28 | assert.equal((await safeMath64Mock.divExt(a, b)).toString(), Math.floor(a / b), 'values should match') 29 | }) 30 | 31 | it('fails dividing by zero', async () => { 32 | const a = 1234 33 | const b = 0 34 | await assertRevert(safeMath64Mock.divExt(a, b)) 35 | }) 36 | 37 | // subtraction 38 | it('subtract', async () => { 39 | const a = 1234 40 | const b = 327 41 | assert.equal((await safeMath64Mock.subExt(a, b)).toString(), a - b, 'values should match') 42 | }) 43 | 44 | it('fails if subtraction underflows', async () => { 45 | const a = 123 46 | const b = 124 47 | await assertRevert(safeMath64Mock.subExt(a, b)) 48 | }) 49 | 50 | // addition 51 | it('adds', async () => { 52 | const a = 1234 53 | const b = 327 54 | assert.equal((await safeMath64Mock.addExt(a, b)).toString(), a + b, 'values should match') 55 | }) 56 | 57 | it('fails if addition overflows', async () => { 58 | const a = bn(2).pow(bn(63)) 59 | const b = bn(2).pow(bn(63)) 60 | await assertRevert(safeMath64Mock.addExt(a, b)) 61 | }) 62 | 63 | // modulo 64 | it('divides modulo', async () => { 65 | const a = 32746 66 | const b = 1234 67 | assert.equal((await safeMath64Mock.modExt(a, b)).toString(), a % b, 'values should match') 68 | }) 69 | 70 | it('fails modulo dividing by zero', async () => { 71 | const a = 1234 72 | const b = 0 73 | await assertRevert(safeMath64Mock.modExt(a, b)) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /contracts/factory/AppProxyFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../apps/AppProxyUpgradeable.sol"; 4 | import "../apps/AppProxyPinned.sol"; 5 | 6 | 7 | contract AppProxyFactory { 8 | event NewAppProxy(address proxy, bool isUpgradeable, bytes32 appId); 9 | 10 | /** 11 | * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId` 12 | * @param _kernel App's Kernel reference 13 | * @param _appId Identifier for app 14 | * @return AppProxyUpgradeable 15 | */ 16 | function newAppProxy(IKernel _kernel, bytes32 _appId) public returns (AppProxyUpgradeable) { 17 | return newAppProxy(_kernel, _appId, new bytes(0)); 18 | } 19 | 20 | /** 21 | * @notice Create a new upgradeable app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload` 22 | * @param _kernel App's Kernel reference 23 | * @param _appId Identifier for app 24 | * @return AppProxyUpgradeable 25 | */ 26 | function newAppProxy(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyUpgradeable) { 27 | AppProxyUpgradeable proxy = new AppProxyUpgradeable(_kernel, _appId, _initializePayload); 28 | emit NewAppProxy(address(proxy), true, _appId); 29 | return proxy; 30 | } 31 | 32 | /** 33 | * @notice Create a new pinned app instance on `_kernel` with identifier `_appId` 34 | * @param _kernel App's Kernel reference 35 | * @param _appId Identifier for app 36 | * @return AppProxyPinned 37 | */ 38 | function newAppProxyPinned(IKernel _kernel, bytes32 _appId) public returns (AppProxyPinned) { 39 | return newAppProxyPinned(_kernel, _appId, new bytes(0)); 40 | } 41 | 42 | /** 43 | * @notice Create a new pinned app instance on `_kernel` with identifier `_appId` and initialization payload `_initializePayload` 44 | * @param _kernel App's Kernel reference 45 | * @param _appId Identifier for app 46 | * @param _initializePayload Proxy initialization payload 47 | * @return AppProxyPinned 48 | */ 49 | function newAppProxyPinned(IKernel _kernel, bytes32 _appId, bytes _initializePayload) public returns (AppProxyPinned) { 50 | AppProxyPinned proxy = new AppProxyPinned(_kernel, _appId, _initializePayload); 51 | emit NewAppProxy(address(proxy), false, _appId); 52 | return proxy; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | 5 | 6 | Version 7 | 8 | 9 | 10 | Security 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 |
19 |

20 | 21 | Website 22 | 23 | | 24 | 25 | Documentation 26 | 27 | | 28 | 29 | Releases 30 | 31 | | 32 | 33 | Contributing 34 | 35 | | 36 | 37 | Support & Chat 38 | 39 |

40 |
41 | 42 | # aragonOS 43 | 44 | A smart contract framework for building decentralized organizations. 45 | 46 | #### 🚨 Security review status: bug bounty 47 | 48 | aragonOS has passed a number of [independent professional security audits](https://wiki.aragon.org/association/security/) with an ongoing $250,000 USD bug bounty program. 49 | 50 | See [SECURITY.md](SECURITY.md) for more security-related information. 51 | 52 | #### 🛰 Deployment status: available on Ethereum mainnet 53 | 54 | aragonOS powers over a thousand organizations on Ethereum mainnet, securing over $350MM in funds. 55 | 56 | Deployment logs for Ethereum mainnet and other networks are available in our [deployment repo](https://github.com/aragon/deployments). 57 | 58 | ## Documentation 59 | 60 | Visit the [Aragon Developer Portal](https://hack.aragon.org/docs/aragonos-intro.html) for in-depth documentation. 61 | 62 | The [reference documentation](https://hack.aragon.org/docs/aragonos-ref.html) explains the various smart contract components and how to use them. 63 | 64 | ## Development 65 | 66 | ```sh 67 | yarn install 68 | yarn test 69 | 70 | # Lint needs to pass as well 71 | yarn run lint 72 | ``` 73 | -------------------------------------------------------------------------------- /buidler.config.js: -------------------------------------------------------------------------------- 1 | const { usePlugin } = require('@nomiclabs/buidler/config') 2 | 3 | usePlugin("@nomiclabs/buidler-ganache") 4 | usePlugin('@nomiclabs/buidler-truffle5') 5 | usePlugin('buidler-gas-reporter') 6 | usePlugin('solidity-coverage') 7 | 8 | module.exports = { 9 | networks: { 10 | // Local development network using ganache. You can set any of the 11 | // Ganache's options. All of them are supported, with the exception 12 | // of accounts. 13 | // https://github.com/trufflesuite/ganache-core#options 14 | ganache: { 15 | url: 'http://localhost:8545', 16 | gasLimit: 6000000000, 17 | defaultBalanceEther: 100 18 | }, 19 | // Local development network to test coverage. Solidity coverage 20 | // pluging launches its own in-process ganache server. 21 | // and expose it at port 8555. 22 | coverage: { 23 | url: 'http://localhost:8555', 24 | }, 25 | // Mainnet network configured with Aragon node. 26 | mainnet: { 27 | url: 'https://mainnet.eth.aragon.network', 28 | accounts: [ 29 | process.env.ETH_KEY || 30 | '0xa8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563', 31 | ], 32 | }, 33 | // Rinkeby network configured with Aragon node. 34 | rinkeby: { 35 | url: 'https://rinkeby.eth.aragon.network', 36 | accounts: [ 37 | process.env.ETH_KEY || 38 | '0xa8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563', 39 | ], 40 | }, 41 | // Network configured to interact with Frame wallet. Requires 42 | // to have Frame running on your machine. Download it from: 43 | // https://frame.sh 44 | frame: { 45 | httpHeaders: { origin: 'buidler' }, 46 | url: 'http://localhost:1248', 47 | } 48 | }, 49 | solc: { 50 | version: '0.4.24', 51 | optimizer: { 52 | enabled: true, 53 | runs: 10000, 54 | }, 55 | }, 56 | // The gas reporter plugin do not properly handle the buidlerevm 57 | // chain yet. In the mean time we should 'npx buidler node' and 58 | // then attach to running process using '--network localhost' as 59 | // explained in: https://buidler.dev/buidler-evm/#connecting-to-buidler-evm-from-wallets-and-other-software. 60 | // You can also run 'yarn devchain' and on a separate terminal run 'yarn test:gas' 61 | gasReporter: { 62 | enabled: process.env.REPORT_GAS ? true : false, 63 | }, 64 | } 65 | -------------------------------------------------------------------------------- /contracts/test/helpers/ACLHelper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../acl/IACLOracle.sol"; 4 | 5 | 6 | contract ACLHelper { 7 | function encodeOperator(uint256 param1, uint256 param2) internal pure returns (uint240) { 8 | return encodeIfElse(param1, param2, 0); 9 | } 10 | 11 | function encodeIfElse(uint256 condition, uint256 successParam, uint256 failureParam) internal pure returns (uint240) { 12 | return uint240(condition + (successParam << 32) + (failureParam << 64)); 13 | } 14 | } 15 | 16 | 17 | contract AcceptOracle is IACLOracle { 18 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 19 | return true; 20 | } 21 | } 22 | 23 | 24 | contract RejectOracle is IACLOracle { 25 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 26 | return false; 27 | } 28 | } 29 | 30 | 31 | contract RevertOracle is IACLOracle { 32 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 33 | revert(); 34 | } 35 | } 36 | 37 | contract AssertOracle is IACLOracle { 38 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 39 | assert(false); 40 | } 41 | } 42 | 43 | // Can't implement from IACLOracle as its canPerform() is marked as view-only 44 | contract StateModifyingOracle /* is IACLOracle */ { 45 | bool modifyState; 46 | 47 | function canPerform(address, address, bytes32, uint256[]) external returns (bool) { 48 | modifyState = true; 49 | return true; 50 | } 51 | } 52 | 53 | contract EmptyDataReturnOracle is IACLOracle { 54 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 55 | assembly { 56 | return(0, 0) 57 | } 58 | } 59 | } 60 | 61 | contract ConditionalOracle is IACLOracle { 62 | function canPerform(address, address, bytes32, uint256[] how) external view returns (bool) { 63 | return how[0] > 0; 64 | } 65 | } 66 | 67 | contract OverGasLimitOracle is IACLOracle { 68 | function canPerform(address, address, bytes32, uint256[]) external view returns (bool) { 69 | while (true) { 70 | // Do an SLOAD to increase the per-loop gas costs 71 | uint256 loadFromStorage; 72 | assembly { loadFromStorage := sload(0) } 73 | } 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /contracts/lib/math/SafeMath8.sol: -------------------------------------------------------------------------------- 1 | // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol 2 | // Adapted for uint8, pragma ^0.4.24, and satisfying our linter rules 3 | // Also optimized the mul() implementation, see https://github.com/aragon/aragonOS/pull/417 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | /** 9 | * @title SafeMath8 10 | * @dev Math operations for uint8 with safety checks that revert on error 11 | */ 12 | library SafeMath8 { 13 | string private constant ERROR_ADD_OVERFLOW = "MATH8_ADD_OVERFLOW"; 14 | string private constant ERROR_SUB_UNDERFLOW = "MATH8_SUB_UNDERFLOW"; 15 | string private constant ERROR_MUL_OVERFLOW = "MATH8_MUL_OVERFLOW"; 16 | string private constant ERROR_DIV_ZERO = "MATH8_DIV_ZERO"; 17 | 18 | /** 19 | * @dev Multiplies two numbers, reverts on overflow. 20 | */ 21 | function mul(uint8 _a, uint8 _b) internal pure returns (uint8) { 22 | uint256 c = uint256(_a) * uint256(_b); 23 | require(c < 256, ERROR_MUL_OVERFLOW); 24 | 25 | return uint8(c); 26 | } 27 | 28 | /** 29 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 30 | */ 31 | function div(uint8 _a, uint8 _b) internal pure returns (uint8) { 32 | require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 33 | uint8 c = _a / _b; 34 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 35 | 36 | return c; 37 | } 38 | 39 | /** 40 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 41 | */ 42 | function sub(uint8 _a, uint8 _b) internal pure returns (uint8) { 43 | require(_b <= _a, ERROR_SUB_UNDERFLOW); 44 | uint8 c = _a - _b; 45 | 46 | return c; 47 | } 48 | 49 | /** 50 | * @dev Adds two numbers, reverts on overflow. 51 | */ 52 | function add(uint8 _a, uint8 _b) internal pure returns (uint8) { 53 | uint8 c = _a + _b; 54 | require(c >= _a, ERROR_ADD_OVERFLOW); 55 | 56 | return c; 57 | } 58 | 59 | /** 60 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 61 | * reverts when dividing by zero. 62 | */ 63 | function mod(uint8 a, uint8 b) internal pure returns (uint8) { 64 | require(b != 0, ERROR_DIV_ZERO); 65 | return a % b; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/contracts/common/lifecycle.js: -------------------------------------------------------------------------------- 1 | const ERRORS = require('../../helpers/errors') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | // Mocks 5 | const LifecycleMock = artifacts.require('LifecycleMock') 6 | 7 | contract('Lifecycle', () => { 8 | let lifecycle 9 | 10 | beforeEach(async () => { 11 | lifecycle = await LifecycleMock.new() 12 | }) 13 | 14 | it('is not initialized', async () => { 15 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 16 | }) 17 | 18 | it('is not petrified', async () => { 19 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 20 | }) 21 | 22 | context('> Initialized', () => { 23 | beforeEach(async () => { 24 | await lifecycle.initializeMock() 25 | }) 26 | 27 | it('is initialized', async () => { 28 | assert.isTrue(await lifecycle.hasInitialized(), 'should be initialized') 29 | }) 30 | 31 | it('is not petrified', async () => { 32 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 33 | }) 34 | 35 | it('has correct initialization block', async () => { 36 | assert.equal(await lifecycle.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') 37 | }) 38 | 39 | it('cannot be re-initialized', async () => { 40 | await assertRevert(lifecycle.initializeMock(), ERRORS.INIT_ALREADY_INITIALIZED) 41 | }) 42 | 43 | it('cannot be petrified', async () => { 44 | await assertRevert(lifecycle.petrifyMock(), ERRORS.INIT_ALREADY_INITIALIZED) 45 | }) 46 | }) 47 | 48 | context('> Petrified', () => { 49 | beforeEach(async () => { 50 | await lifecycle.petrifyMock() 51 | }) 52 | 53 | it('is not initialized', async () => { 54 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 55 | }) 56 | 57 | it('is petrified', async () => { 58 | assert.isTrue(await lifecycle.isPetrified(), 'should be petrified') 59 | }) 60 | 61 | it('cannot be petrified again', async () => { 62 | await assertRevert(lifecycle.petrifyMock(), ERRORS.INIT_ALREADY_INITIALIZED) 63 | }) 64 | 65 | it('has initialization block in the future', async () => { 66 | const petrifiedBlock = await lifecycle.getInitializationBlock() 67 | const blockNumber = await web3.eth.getBlockNumber() 68 | assert.isTrue(petrifiedBlock.gt(blockNumber), 'petrified block should be in the future') 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /contracts/lib/math/SafeMath64.sol: -------------------------------------------------------------------------------- 1 | // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol 2 | // Adapted for uint64, pragma ^0.4.24, and satisfying our linter rules 3 | // Also optimized the mul() implementation, see https://github.com/aragon/aragonOS/pull/417 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | /** 9 | * @title SafeMath64 10 | * @dev Math operations for uint64 with safety checks that revert on error 11 | */ 12 | library SafeMath64 { 13 | string private constant ERROR_ADD_OVERFLOW = "MATH64_ADD_OVERFLOW"; 14 | string private constant ERROR_SUB_UNDERFLOW = "MATH64_SUB_UNDERFLOW"; 15 | string private constant ERROR_MUL_OVERFLOW = "MATH64_MUL_OVERFLOW"; 16 | string private constant ERROR_DIV_ZERO = "MATH64_DIV_ZERO"; 17 | 18 | /** 19 | * @dev Multiplies two numbers, reverts on overflow. 20 | */ 21 | function mul(uint64 _a, uint64 _b) internal pure returns (uint64) { 22 | uint256 c = uint256(_a) * uint256(_b); 23 | require(c < 0x010000000000000000, ERROR_MUL_OVERFLOW); // 2**64 (less gas this way) 24 | 25 | return uint64(c); 26 | } 27 | 28 | /** 29 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 30 | */ 31 | function div(uint64 _a, uint64 _b) internal pure returns (uint64) { 32 | require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 33 | uint64 c = _a / _b; 34 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 35 | 36 | return c; 37 | } 38 | 39 | /** 40 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 41 | */ 42 | function sub(uint64 _a, uint64 _b) internal pure returns (uint64) { 43 | require(_b <= _a, ERROR_SUB_UNDERFLOW); 44 | uint64 c = _a - _b; 45 | 46 | return c; 47 | } 48 | 49 | /** 50 | * @dev Adds two numbers, reverts on overflow. 51 | */ 52 | function add(uint64 _a, uint64 _b) internal pure returns (uint64) { 53 | uint64 c = _a + _b; 54 | require(c >= _a, ERROR_ADD_OVERFLOW); 55 | 56 | return c; 57 | } 58 | 59 | /** 60 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 61 | * reverts when dividing by zero. 62 | */ 63 | function mod(uint64 a, uint64 b) internal pure returns (uint64) { 64 | require(b != 0, ERROR_DIV_ZERO); 65 | return a % b; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/helpers/runSolidityTest.js: -------------------------------------------------------------------------------- 1 | const { getEventAt } = require('@aragon/contract-helpers-test') 2 | 3 | const ASSERT_LIB_EVENTS_ABI = [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": true, 9 | "name": "result", 10 | "type": "bool" 11 | }, 12 | { 13 | "indexed": false, 14 | "name": "message", 15 | "type": "string" 16 | } 17 | ], 18 | "name": "TestEvent", 19 | "type": "event" 20 | }, 21 | ] 22 | 23 | const HOOKS_MAP = { 24 | beforeAll: 'before', 25 | beforeEach: 'beforeEach', 26 | afterEach: 'afterEach', 27 | afterAll: 'afterAll', 28 | } 29 | 30 | /* 31 | * Deploy and link `libName` to provided contract artifact 32 | */ 33 | const linkLib = async (contract, libName) => { 34 | const library = await artifacts.require(libName).new() 35 | await contract.link(library) 36 | } 37 | 38 | /** 39 | * Runs a solidity test file, via javascript. 40 | * Required to smooth over some technical problems in solidity-coverage 41 | * 42 | * @param {string} testContract Name of solidity test file 43 | */ 44 | function runSolidityTest(testContract, mochaContext) { 45 | const artifact = artifacts.require(testContract) 46 | 47 | contract(testContract, () => { 48 | let deployed 49 | 50 | before(async () => { 51 | await linkLib(artifact, 'Assert') 52 | deployed = await artifact.new() 53 | }) 54 | 55 | const assertResult = async call => { 56 | const receipt = await call() 57 | const { args: { result, message } } = getEventAt(receipt, 'TestEvent', { decodeForAbi: ASSERT_LIB_EVENTS_ABI }) 58 | if (!result) throw new Error(message || 'No assertions made') 59 | } 60 | 61 | mochaContext('> Solidity test', () => { 62 | artifact.abi.forEach(interface => { 63 | if (interface.type === 'function') { 64 | // Set up hooks 65 | if (['beforeAll', 'beforeEach', 'afterEach', 'afterAll'].includes(interface.name)) { 66 | global[HOOKS_MAP[interface.name]](async () => await deployed[interface.name]()) 67 | } else if (interface.name.startsWith('test')) { 68 | it(interface.name, async () => await assertResult(deployed[interface.name])) 69 | } 70 | } 71 | }) 72 | }) 73 | }) 74 | } 75 | 76 | // Bind the functions for ease of use, and provide .only() and .skip() hooks 77 | const fn = (c) => runSolidityTest(c, context) 78 | fn.only = (c) => runSolidityTest(c, context.only) 79 | fn.skip = (c) => runSolidityTest(c, context.skip) 80 | 81 | module.exports = fn 82 | -------------------------------------------------------------------------------- /contracts/lib/math/SafeMath.sol: -------------------------------------------------------------------------------- 1 | // See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol 2 | // Adapted to use pragma ^0.4.24 and satisfy our linter rules 3 | 4 | pragma solidity ^0.4.24; 5 | 6 | 7 | /** 8 | * @title SafeMath 9 | * @dev Math operations with safety checks that revert on error 10 | */ 11 | library SafeMath { 12 | string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW"; 13 | string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW"; 14 | string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW"; 15 | string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO"; 16 | 17 | /** 18 | * @dev Multiplies two numbers, reverts on overflow. 19 | */ 20 | function mul(uint256 _a, uint256 _b) internal pure returns (uint256) { 21 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 22 | // benefit is lost if 'b' is also tested. 23 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 24 | if (_a == 0) { 25 | return 0; 26 | } 27 | 28 | uint256 c = _a * _b; 29 | require(c / _a == _b, ERROR_MUL_OVERFLOW); 30 | 31 | return c; 32 | } 33 | 34 | /** 35 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 36 | */ 37 | function div(uint256 _a, uint256 _b) internal pure returns (uint256) { 38 | require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 39 | uint256 c = _a / _b; 40 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 41 | 42 | return c; 43 | } 44 | 45 | /** 46 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 47 | */ 48 | function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { 49 | require(_b <= _a, ERROR_SUB_UNDERFLOW); 50 | uint256 c = _a - _b; 51 | 52 | return c; 53 | } 54 | 55 | /** 56 | * @dev Adds two numbers, reverts on overflow. 57 | */ 58 | function add(uint256 _a, uint256 _b) internal pure returns (uint256) { 59 | uint256 c = _a + _b; 60 | require(c >= _a, ERROR_ADD_OVERFLOW); 61 | 62 | return c; 63 | } 64 | 65 | /** 66 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 67 | * reverts when dividing by zero. 68 | */ 69 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 70 | require(b != 0, ERROR_DIV_ZERO); 71 | return a % b; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /contracts/test/mocks/apps/disputable/DisputableAppMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../../../../apps/disputable/DisputableAragonApp.sol"; 4 | 5 | 6 | contract DisputableAppMock is DisputableAragonApp { 7 | bytes4 public constant DISPUTABLE_INTERFACE = DISPUTABLE_INTERFACE_ID; 8 | 9 | event DisputableChallenged(uint256 indexed id); 10 | event DisputableAllowed(uint256 indexed id); 11 | event DisputableRejected(uint256 indexed id); 12 | event DisputableVoided(uint256 indexed id); 13 | 14 | function initialize() external { 15 | initialized(); 16 | } 17 | 18 | function newAction(uint256 _disputableActionId, bytes _context, address _submitter) external { 19 | _registerDisputableAction(_disputableActionId, _context, _submitter); 20 | } 21 | 22 | function closeAction(uint256 _actionId) external { 23 | _closeDisputableAction(_actionId); 24 | } 25 | 26 | function canChallenge(uint256 /*_disputableActionId*/) external view returns (bool) { 27 | return true; 28 | } 29 | 30 | function canClose(uint256 /*_disputableActionId*/) external view returns (bool) { 31 | return true; 32 | } 33 | 34 | function interfaceID() external pure returns (bytes4) { 35 | IDisputable iDisputable; 36 | return iDisputable.setAgreement.selector ^ 37 | iDisputable.onDisputableActionChallenged.selector ^ 38 | iDisputable.onDisputableActionAllowed.selector ^ 39 | iDisputable.onDisputableActionRejected.selector ^ 40 | iDisputable.onDisputableActionVoided.selector ^ 41 | iDisputable.getAgreement.selector ^ 42 | iDisputable.canChallenge.selector ^ 43 | iDisputable.canClose.selector; 44 | } 45 | 46 | /** 47 | * @dev Challenge an entry 48 | * @param _id Identifier of the entry to be challenged 49 | */ 50 | function _onDisputableActionChallenged(uint256 _id, uint256 /* _challengeId */, address /* _challenger */) internal { 51 | emit DisputableChallenged(_id); 52 | } 53 | 54 | /** 55 | * @dev Allow an entry 56 | * @param _id Identifier of the entry to be allowed 57 | */ 58 | function _onDisputableActionAllowed(uint256 _id) internal { 59 | emit DisputableAllowed(_id); 60 | } 61 | 62 | /** 63 | * @dev Reject an entry 64 | * @param _id Identifier of the entry to be rejected 65 | */ 66 | function _onDisputableActionRejected(uint256 _id) internal { 67 | emit DisputableRejected(_id); 68 | } 69 | 70 | /** 71 | * @dev Void an entry 72 | * @param _id Identifier of the entry to be voided 73 | */ 74 | function _onDisputableActionVoided(uint256 _id) internal { 75 | emit DisputableVoided(_id); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/factory/DAOFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../kernel/IKernel.sol"; 4 | import "../kernel/Kernel.sol"; 5 | import "../kernel/KernelProxy.sol"; 6 | 7 | import "../acl/IACL.sol"; 8 | import "../acl/ACL.sol"; 9 | 10 | import "./EVMScriptRegistryFactory.sol"; 11 | 12 | 13 | contract DAOFactory { 14 | IKernel public baseKernel; 15 | IACL public baseACL; 16 | EVMScriptRegistryFactory public regFactory; 17 | 18 | event DeployDAO(address dao); 19 | event DeployEVMScriptRegistry(address reg); 20 | 21 | /** 22 | * @notice Create a new DAOFactory, creating DAOs with Kernels proxied to `_baseKernel`, ACLs proxied to `_baseACL`, and new EVMScriptRegistries created from `_regFactory`. 23 | * @param _baseKernel Base Kernel 24 | * @param _baseACL Base ACL 25 | * @param _regFactory EVMScriptRegistry factory 26 | */ 27 | constructor(IKernel _baseKernel, IACL _baseACL, EVMScriptRegistryFactory _regFactory) public { 28 | // No need to init as it cannot be killed by devops199 29 | if (address(_regFactory) != address(0)) { 30 | regFactory = _regFactory; 31 | } 32 | 33 | baseKernel = _baseKernel; 34 | baseACL = _baseACL; 35 | } 36 | 37 | /** 38 | * @notice Create a new DAO with `_root` set as the initial admin 39 | * @param _root Address that will be granted control to setup DAO permissions 40 | * @return Newly created DAO 41 | */ 42 | function newDAO(address _root) public returns (Kernel) { 43 | Kernel dao = Kernel(new KernelProxy(baseKernel)); 44 | 45 | if (address(regFactory) == address(0)) { 46 | dao.initialize(baseACL, _root); 47 | } else { 48 | dao.initialize(baseACL, this); 49 | 50 | ACL acl = ACL(dao.acl()); 51 | bytes32 permRole = acl.CREATE_PERMISSIONS_ROLE(); 52 | bytes32 appManagerRole = dao.APP_MANAGER_ROLE(); 53 | 54 | acl.grantPermission(regFactory, acl, permRole); 55 | 56 | acl.createPermission(regFactory, dao, appManagerRole, this); 57 | 58 | EVMScriptRegistry reg = regFactory.newEVMScriptRegistry(dao); 59 | emit DeployEVMScriptRegistry(address(reg)); 60 | 61 | // Clean up permissions 62 | // First, completely reset the APP_MANAGER_ROLE 63 | acl.revokePermission(regFactory, dao, appManagerRole); 64 | acl.removePermissionManager(dao, appManagerRole); 65 | 66 | // Then, make root the only holder and manager of CREATE_PERMISSIONS_ROLE 67 | acl.revokePermission(regFactory, acl, permRole); 68 | acl.revokePermission(this, acl, permRole); 69 | acl.grantPermission(_root, acl, permRole); 70 | acl.setPermissionManager(_root, acl, permRole); 71 | } 72 | 73 | emit DeployDAO(address(dao)); 74 | 75 | return dao; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/test/mocks/common/KeccakConstants.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | contract KeccakConstants { 5 | // ENS 6 | bytes32 public constant ENS_ROOT = bytes32(0); 7 | bytes32 public constant ETH_TLD_LABEL = keccak256(abi.encodePacked("eth")); 8 | bytes32 public constant ETH_TLD_NODE = keccak256(abi.encodePacked(ENS_ROOT, ETH_TLD_LABEL)); 9 | bytes32 public constant PUBLIC_RESOLVER_LABEL = keccak256(abi.encodePacked("resolver")); 10 | bytes32 public constant PUBLIC_RESOLVER_NODE = keccak256(abi.encodePacked(ETH_TLD_NODE, PUBLIC_RESOLVER_LABEL)); 11 | 12 | // APM 13 | bytes32 public constant APM_NODE = keccak256(abi.encodePacked(ETH_TLD_NODE, keccak256(abi.encodePacked("aragonpm")))); 14 | 15 | // Kernel 16 | bytes32 public constant KERNEL_CORE_NAMESPACE = keccak256(abi.encodePacked("core")); 17 | bytes32 public constant KERNEL_APP_BASES_NAMESPACE = keccak256(abi.encodePacked("base")); 18 | bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = keccak256(abi.encodePacked("app")); 19 | 20 | bytes32 public constant APP_MANAGER_ROLE = keccak256(abi.encodePacked("APP_MANAGER_ROLE")); 21 | 22 | bytes32 public constant KERNEL_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("kernel"))); 23 | bytes32 public constant DEFAULT_ACL_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("acl"))); 24 | bytes32 public constant DEFAULT_VAULT_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("vault"))); 25 | 26 | // ACL 27 | bytes32 public constant CREATE_PERMISSIONS_ROLE = keccak256(abi.encodePacked("CREATE_PERMISSIONS_ROLE")); 28 | bytes32 public constant EMPTY_PARAM_HASH = keccak256(abi.encodePacked(uint256(0))); 29 | 30 | // EVMScriptRegistry 31 | bytes32 public constant EVMSCRIPT_REGISTRY_APP_ID = keccak256(abi.encodePacked(APM_NODE, keccak256("evmreg"))); 32 | bytes32 public constant REGISTRY_ADD_EXECUTOR_ROLE = keccak256("REGISTRY_ADD_EXECUTOR_ROLE"); 33 | bytes32 public constant REGISTRY_MANAGER_ROLE = keccak256(abi.encodePacked("REGISTRY_MANAGER_ROLE")); 34 | 35 | // EVMScriptExecutor types 36 | bytes32 public constant EVMSCRIPT_EXECUTOR_CALLS_SCRIPT = keccak256(abi.encodePacked("CALLS_SCRIPT")); 37 | 38 | // Unstructured storage 39 | bytes32 public constant initializationBlockPosition = keccak256(abi.encodePacked("aragonOS.initializable.initializationBlock")); 40 | bytes32 public constant depositablePosition = keccak256(abi.encodePacked("aragonOS.depositableStorage.depositable")); 41 | bytes32 public constant reentrancyGuardPosition = keccak256(abi.encodePacked("aragonOS.reentrancyGuard.mutex")); 42 | bytes32 public constant kernelPosition = keccak256(abi.encodePacked("aragonOS.appStorage.kernel")); 43 | bytes32 public constant appIdPosition = keccak256(abi.encodePacked("aragonOS.appStorage.appId")); 44 | bytes32 public constant pinnedCodePosition = keccak256(abi.encodePacked("aragonOS.appStorage.pinnedCode")); 45 | } 46 | -------------------------------------------------------------------------------- /contracts/apps/AragonApp.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./AppStorage.sol"; 8 | import "../acl/ACLSyntaxSugar.sol"; 9 | import "../common/Autopetrified.sol"; 10 | import "../common/ConversionHelpers.sol"; 11 | import "../common/ReentrancyGuard.sol"; 12 | import "../common/VaultRecoverable.sol"; 13 | import "../evmscript/EVMScriptRunner.sol"; 14 | import "../lib/standards/ERC165.sol"; 15 | 16 | 17 | // Contracts inheriting from AragonApp are, by default, immediately petrified upon deployment so 18 | // that they can never be initialized. 19 | // Unless overriden, this behaviour enforces those contracts to be usable only behind an AppProxy. 20 | // ReentrancyGuard, EVMScriptRunner, and ACLSyntaxSugar are not directly used by this contract, but 21 | // are included so that they are automatically usable by subclassing contracts 22 | contract AragonApp is ERC165, AppStorage, Autopetrified, VaultRecoverable, ReentrancyGuard, EVMScriptRunner, ACLSyntaxSugar { 23 | string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED"; 24 | 25 | modifier auth(bytes32 _role) { 26 | require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED); 27 | _; 28 | } 29 | 30 | modifier authP(bytes32 _role, uint256[] _params) { 31 | require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED); 32 | _; 33 | } 34 | 35 | /** 36 | * @dev Check whether an action can be performed by a sender for a particular role on this app 37 | * @param _sender Sender of the call 38 | * @param _role Role on this app 39 | * @param _params Permission params for the role 40 | * @return Boolean indicating whether the sender has the permissions to perform the action. 41 | * Always returns false if the app hasn't been initialized yet. 42 | */ 43 | function canPerform(address _sender, bytes32 _role, uint256[] _params) public view returns (bool) { 44 | if (!hasInitialized()) { 45 | return false; 46 | } 47 | 48 | IKernel linkedKernel = kernel(); 49 | if (address(linkedKernel) == address(0)) { 50 | return false; 51 | } 52 | 53 | return linkedKernel.hasPermission( 54 | _sender, 55 | address(this), 56 | _role, 57 | ConversionHelpers.dangerouslyCastUintArrayToBytes(_params) 58 | ); 59 | } 60 | 61 | /** 62 | * @dev Get the recovery vault for the app 63 | * @return Recovery vault address for the app 64 | */ 65 | function getRecoveryVault() public view returns (address) { 66 | // Funds recovery via a vault is only available when used with a kernel 67 | return kernel().getRecoveryVault(); // if kernel is not set, it will revert 68 | } 69 | 70 | /** 71 | * @dev Query if a contract implements a certain interface 72 | * @param _interfaceId The interface identifier being queried, as specified in ERC-165 73 | * @return True if the contract implements the requested interface and if its not 0xffffffff, false otherwise 74 | */ 75 | function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { 76 | return super.supportsInterface(_interfaceId) || _interfaceId == ARAGON_APP_INTERFACE_ID; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /contracts/acl/ACLSyntaxSugar.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | 8 | contract ACLSyntaxSugar { 9 | function arr() internal pure returns (uint256[]) { 10 | return new uint256[](0); 11 | } 12 | 13 | function arr(bytes32 _a) internal pure returns (uint256[] r) { 14 | return arr(uint256(_a)); 15 | } 16 | 17 | function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) { 18 | return arr(uint256(_a), uint256(_b)); 19 | } 20 | 21 | function arr(address _a) internal pure returns (uint256[] r) { 22 | return arr(uint256(_a)); 23 | } 24 | 25 | function arr(address _a, address _b) internal pure returns (uint256[] r) { 26 | return arr(uint256(_a), uint256(_b)); 27 | } 28 | 29 | function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) { 30 | return arr(uint256(_a), _b, _c); 31 | } 32 | 33 | function arr(address _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) { 34 | return arr(uint256(_a), _b, _c, _d); 35 | } 36 | 37 | function arr(address _a, uint256 _b) internal pure returns (uint256[] r) { 38 | return arr(uint256(_a), uint256(_b)); 39 | } 40 | 41 | function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) { 42 | return arr(uint256(_a), uint256(_b), _c, _d, _e); 43 | } 44 | 45 | function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) { 46 | return arr(uint256(_a), uint256(_b), uint256(_c)); 47 | } 48 | 49 | function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) { 50 | return arr(uint256(_a), uint256(_b), uint256(_c)); 51 | } 52 | 53 | function arr(uint256 _a) internal pure returns (uint256[] r) { 54 | r = new uint256[](1); 55 | r[0] = _a; 56 | } 57 | 58 | function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) { 59 | r = new uint256[](2); 60 | r[0] = _a; 61 | r[1] = _b; 62 | } 63 | 64 | function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) { 65 | r = new uint256[](3); 66 | r[0] = _a; 67 | r[1] = _b; 68 | r[2] = _c; 69 | } 70 | 71 | function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) { 72 | r = new uint256[](4); 73 | r[0] = _a; 74 | r[1] = _b; 75 | r[2] = _c; 76 | r[3] = _d; 77 | } 78 | 79 | function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) { 80 | r = new uint256[](5); 81 | r[0] = _a; 82 | r[1] = _b; 83 | r[2] = _c; 84 | r[3] = _d; 85 | r[4] = _e; 86 | } 87 | } 88 | 89 | 90 | contract ACLHelpers { 91 | function decodeParamOp(uint256 _x) internal pure returns (uint8 b) { 92 | return uint8(_x >> (8 * 30)); 93 | } 94 | 95 | function decodeParamId(uint256 _x) internal pure returns (uint8 b) { 96 | return uint8(_x >> (8 * 31)); 97 | } 98 | 99 | function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) { 100 | a = uint32(_x); 101 | b = uint32(_x >> (8 * 4)); 102 | c = uint32(_x >> (8 * 8)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/contracts/kernel/kernel_upgrade.js: -------------------------------------------------------------------------------- 1 | const { ZERO_ADDRESS } = require('@aragon/contract-helpers-test') 2 | const { assertEvent, assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | const ACL = artifacts.require('ACL') 5 | const Kernel = artifacts.require('Kernel') 6 | const KernelProxy = artifacts.require('KernelProxy') 7 | const UpgradedKernel = artifacts.require('UpgradedKernel') 8 | 9 | // Mocks 10 | const ERCProxyMock = artifacts.require('ERCProxyMock') 11 | 12 | // Only applicable to KernelProxy instances 13 | contract('Kernel upgrade', ([permissionsRoot, someone]) => { 14 | let aclBase, kernelBase, upgradedBase, kernelAddr, kernel, acl 15 | let APP_MANAGER_ROLE, CORE_NAMESPACE, KERNEL_APP_ID, UPGRADEABLE 16 | 17 | before(async () => { 18 | kernelBase = await Kernel.new(true) // petrify immediately 19 | upgradedBase = await UpgradedKernel.new(true) // petrify immediately 20 | 21 | aclBase = await ACL.new() 22 | 23 | // Setup constants 24 | APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE() 25 | CORE_NAMESPACE = await kernelBase.CORE_NAMESPACE() 26 | KERNEL_APP_ID = await kernelBase.KERNEL_APP_ID() 27 | 28 | const ercProxyMock = await ERCProxyMock.new() 29 | UPGRADEABLE = (await ercProxyMock.UPGRADEABLE()).toString() 30 | }) 31 | 32 | beforeEach(async () => { 33 | kernel = await Kernel.at((await KernelProxy.new(kernelBase.address)).address) 34 | await kernel.initialize(aclBase.address, permissionsRoot) 35 | acl = await ACL.at(await kernel.acl()) 36 | kernelAddr = kernel.address 37 | }) 38 | 39 | it('checks ERC897 functions', async () => { 40 | const kernelProxy = await KernelProxy.at(kernelAddr) 41 | const implementation = await kernelProxy.implementation() 42 | assert.equal(implementation, kernelBase.address, "App address should match") 43 | const proxyType = (await kernelProxy.proxyType()).toString() 44 | assert.equal(proxyType, UPGRADEABLE, "Proxy type should be 2 (upgradeable)") 45 | }) 46 | 47 | it('emits SetApp event', async () => { 48 | const kernelProxy = await KernelProxy.new(kernelBase.address) 49 | const { logs } = await web3.eth.getTransactionReceipt(kernelProxy.transactionHash) 50 | 51 | assertEvent({ rawLogs: logs }, 'SetApp', { decodeForAbi: KernelProxy.abi, expectedArgs: { namespace: CORE_NAMESPACE, appId: KERNEL_APP_ID, app: kernelBase.address } }) 52 | }) 53 | 54 | it('fails to create a KernelProxy if the base is 0', async () => { 55 | await assertRevert(KernelProxy.new(ZERO_ADDRESS)) 56 | }) 57 | 58 | it('fails to create a KernelProxy if the base is not a contract', async () => { 59 | await assertRevert(KernelProxy.new(someone)) 60 | }) 61 | 62 | it('fails to upgrade kernel without permission', async () => { 63 | await assertRevert(kernel.setApp(CORE_NAMESPACE, KERNEL_APP_ID, permissionsRoot)) 64 | }) 65 | 66 | it('fails when calling upgraded functionality on old version', async () => { 67 | await assertRevert((await UpgradedKernel.at(kernelAddr)).isUpgraded()) 68 | }) 69 | 70 | it('successfully upgrades kernel', async () => { 71 | await acl.createPermission(permissionsRoot, kernelAddr, APP_MANAGER_ROLE, permissionsRoot, { from: permissionsRoot }) 72 | 73 | await kernel.setApp(CORE_NAMESPACE, KERNEL_APP_ID, upgradedBase.address) 74 | 75 | assert.isTrue(await (await UpgradedKernel.at(kernelAddr)).isUpgraded(), 'kernel should have been upgraded') 76 | }) 77 | 78 | it('fails if upgrading to kernel that is not a contract', async () => { 79 | await acl.createPermission(permissionsRoot, kernelAddr, APP_MANAGER_ROLE, permissionsRoot, { from: permissionsRoot }) 80 | 81 | await assertRevert(kernel.setApp(CORE_NAMESPACE, KERNEL_APP_ID, someone)) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/contracts/kernel/kernel_funds.js: -------------------------------------------------------------------------------- 1 | const { bn, onlyIf } = require('@aragon/contract-helpers-test') 2 | const { assertBn, assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | const ACL = artifacts.require('ACL') 5 | const Kernel = artifacts.require('Kernel') 6 | const KernelProxy = artifacts.require('KernelProxy') 7 | const KernelDepositableMock = artifacts.require('KernelDepositableMock') 8 | 9 | const TX_BASE_GAS = 21000 10 | const SEND_ETH_GAS = TX_BASE_GAS + 9999 // <10k gas is the threshold for depositing 11 | 12 | contract('Kernel funds', ([permissionsRoot]) => { 13 | let aclBase 14 | 15 | // Initial setup 16 | before(async () => { 17 | aclBase = await ACL.new() 18 | }) 19 | 20 | for (const kernelBaseType of [Kernel, KernelDepositableMock]) { 21 | context(`> ${kernelBaseType.contractName}`, () => { 22 | const onlyKernelDepositable = onlyIf(() => kernelBaseType === KernelDepositableMock) 23 | 24 | // Test both the base itself and the KernelProxy to make sure their behaviours are the same 25 | for (const kernelType of ['Base', 'Proxy']) { 26 | context(`> ${kernelType}`, () => { 27 | let kernelBase, kernel 28 | 29 | before(async () => { 30 | if (kernelType === 'Proxy') { 31 | // We can reuse the same kernel base for the proxies 32 | kernelBase = await kernelBaseType.new(true) // petrify immediately 33 | } 34 | }) 35 | 36 | beforeEach(async () => { 37 | if (kernelType === 'Base') { 38 | kernel = await kernelBaseType.new(false) // don't petrify so it can be used 39 | } else if (kernelType === 'Proxy') { 40 | kernel = await kernelBaseType.at((await KernelProxy.new(kernelBase.address)).address) 41 | } 42 | }) 43 | 44 | it('cannot receive ETH', async () => { 45 | // Before initialization 46 | assert.isFalse(await kernel.hasInitialized(), 'should not have been initialized') 47 | 48 | await assertRevert(kernel.sendTransaction({ value: 1, gas: SEND_ETH_GAS })) 49 | 50 | // After initialization 51 | await kernel.initialize(aclBase.address, permissionsRoot) 52 | assert.isTrue(await kernel.hasInitialized(), 'should have been initialized') 53 | 54 | await assertRevert(kernel.sendTransaction({ value: 1, gas: SEND_ETH_GAS })) 55 | }) 56 | 57 | onlyKernelDepositable(() => { 58 | it('does not have depositing enabled by default', async () => { 59 | // Before initialization 60 | assert.isFalse(await kernel.hasInitialized(), 'should not have been initialized') 61 | assert.isFalse(await kernel.isDepositable(), 'should not be depositable') 62 | 63 | // After initialization 64 | await kernel.initialize(aclBase.address, permissionsRoot) 65 | assert.isTrue(await kernel.hasInitialized(), 'should have been initialized') 66 | assert.isFalse(await kernel.isDepositable(), 'should not be depositable') 67 | }) 68 | 69 | it('can receive ETH after being enabled', async () => { 70 | const amount = bn(1) 71 | const initialBalance = bn(await web3.eth.getBalance(kernel.address)) 72 | 73 | await kernel.initialize(aclBase.address, permissionsRoot) 74 | await kernel.enableDeposits() 75 | assert.isTrue(await kernel.hasInitialized(), 'should have been initialized') 76 | assert.isTrue(await kernel.isDepositable(), 'should be depositable') 77 | 78 | await kernel.sendTransaction({ value: amount, gas: SEND_ETH_GAS }) 79 | assertBn(bn(await web3.eth.getBalance(kernel.address)), initialBalance.add(amount)) 80 | }) 81 | }) 82 | }) 83 | } 84 | }) 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /test/contracts/common/reentrancy_guard.js: -------------------------------------------------------------------------------- 1 | const { ZERO_ADDRESS } = require('@aragon/contract-helpers-test') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | contract('ReentrancyGuard', () => { 5 | let reentrancyMock 6 | 7 | async function assertReentrancyMutex(mutex, msg) { 8 | assert.equal( 9 | await web3.eth.getStorageAt(reentrancyMock.address, (await reentrancyMock.getReentrancyMutexPosition())), 10 | mutex, 11 | msg 12 | ) 13 | } 14 | 15 | beforeEach(async () => { 16 | reentrancyMock = await artifacts.require('ReentrancyGuardMock').new() 17 | }) 18 | 19 | it('starts with false mutex', async () => { 20 | await assertReentrancyMutex(false, 're-entrancy mutex should start false') 21 | }) 22 | 23 | it('starts with no calls', async () => { 24 | assert.equal((await reentrancyMock.callCounter()).toString(), 0, 'should start with no calls') 25 | }) 26 | 27 | it('can call re-entrant function normally', async () => { 28 | await reentrancyMock.reentrantCall(ZERO_ADDRESS) 29 | assert.equal((await reentrancyMock.callCounter()).toString(), 1, 'should have registered one call') 30 | await assertReentrancyMutex(false, 're-entrancy mutex should end false') 31 | }) 32 | 33 | it('can call non-re-entrant function normally', async () => { 34 | await reentrancyMock.nonReentrantCall(ZERO_ADDRESS) 35 | assert.equal((await reentrancyMock.callCounter()).toString(), 1, 'should have registered one call') 36 | await assertReentrancyMutex(false, 're-entrancy mutex should end false') 37 | }) 38 | 39 | context('> Enabled re-entrancy mutex', () => { 40 | beforeEach(async () => { 41 | // Manually set re-entrancy mutex 42 | await reentrancyMock.setReentrancyMutex(true) 43 | }) 44 | 45 | it('can call re-entrant function if re-entrancy mutex is enabled', async () => { 46 | await reentrancyMock.reentrantCall(ZERO_ADDRESS) 47 | assert.equal((await reentrancyMock.callCounter()).toString(), 1, 'should have called') 48 | }) 49 | 50 | it('can not call non-re-entrant function if re-entrancy mutex is enabled', async () => { 51 | await assertRevert(reentrancyMock.nonReentrantCall(ZERO_ADDRESS)) 52 | assert.equal((await reentrancyMock.callCounter()).toString(), 0, 'should not have called') 53 | }) 54 | }) 55 | 56 | context('> Re-entering through contract', async () => { 57 | let reentrantActor 58 | 59 | afterEach(async () => { 60 | await assertReentrancyMutex(false, 're-entrancy mutex should end false') 61 | }) 62 | 63 | context('> Re-enters re-entrant call', async () => { 64 | before(async () => { 65 | reentrantActor = await artifacts.require('ReentrantActor').new(false) 66 | }) 67 | 68 | it('allows re-entering re-entrant call', async () => { 69 | await reentrancyMock.reentrantCall(reentrantActor.address) 70 | assert.equal((await reentrancyMock.callCounter()).toString(), 2, 'should have called twice') 71 | }) 72 | 73 | it('allows entering non-re-entrant call from re-entrant call', async () => { 74 | await reentrancyMock.nonReentrantCall(reentrantActor.address) 75 | assert.equal((await reentrancyMock.callCounter()).toString(), 2, 'should have called twice') 76 | }) 77 | }) 78 | 79 | context('> Re-enters non-reentrant call', async () => { 80 | before(async () => { 81 | reentrantActor = await artifacts.require('ReentrantActor').new(true) 82 | }) 83 | 84 | it('disallows re-entering non-re-entrant call', async () => { 85 | await assertRevert(reentrancyMock.nonReentrantCall(reentrantActor.address)) 86 | assert.equal((await reentrancyMock.callCounter()).toString(), 0, 'should not have completed any calls') 87 | }) 88 | 89 | it('allows entering non-entrant call from re-entrant call', async () => { 90 | await reentrancyMock.reentrantCall(reentrantActor.address) 91 | assert.equal((await reentrancyMock.callCounter()).toString(), 2, 'should have called twice') 92 | }) 93 | }) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /test/contracts/apps/app_base_lifecycle.js: -------------------------------------------------------------------------------- 1 | const { sha3 } = require('web3-utils') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | const { ZERO_ADDRESS } = require('@aragon/contract-helpers-test') 4 | 5 | const ACL = artifacts.require('ACL') 6 | const Kernel = artifacts.require('Kernel') 7 | const KernelProxy = artifacts.require('KernelProxy') 8 | const AragonApp = artifacts.require('AragonApp') 9 | 10 | // Mocks 11 | const UnsafeAragonAppMock = artifacts.require('UnsafeAragonAppMock') 12 | 13 | const FAKE_ROLE = sha3('FAKE_ROLE') 14 | 15 | contract('App base lifecycle', ([permissionsRoot]) => { 16 | let aclBase, kernelBase 17 | 18 | before(async () => { 19 | kernelBase = await Kernel.new(true) // petrify immediately 20 | aclBase = await ACL.new() 21 | }) 22 | 23 | context('> AragonApp', () => { 24 | let app 25 | 26 | beforeEach(async () => { 27 | app = await AragonApp.new() 28 | }) 29 | 30 | it('is not initialized', async () => { 31 | assert.isFalse(await app.hasInitialized(), 'should not be initialized') 32 | }) 33 | 34 | it('is petrified', async () => { 35 | assert.isTrue(await app.isPetrified(), 'should be petrified') 36 | }) 37 | 38 | it('does not have initialization function', async () => { 39 | assert.isNotFunction(app.initialize, 'base AragonApp should not have initialize') 40 | }) 41 | 42 | it('should not be usable', async () => { 43 | assert.isFalse(await app.canPerform(permissionsRoot, FAKE_ROLE, [])) 44 | }) 45 | }) 46 | 47 | context('> UnsafeAragonApp', () => { 48 | let app 49 | 50 | beforeEach(async () => { 51 | // Use the mock so we can initialize and set the kernel 52 | app = await UnsafeAragonAppMock.new() 53 | }) 54 | 55 | it('is not initialized by default', async () => { 56 | assert.isFalse(await app.hasInitialized(), 'should not be initialized') 57 | }) 58 | 59 | it('is not petrified by default', async () => { 60 | assert.isFalse(await app.isPetrified(), 'should not be petrified') 61 | }) 62 | 63 | it('should not be usable yet', async () => { 64 | assert.isFalse(await app.canPerform(permissionsRoot, FAKE_ROLE, [])) 65 | }) 66 | 67 | context('> Initialized', () => { 68 | beforeEach(async () => { 69 | await app.initialize() 70 | }) 71 | 72 | it('is now initialized', async () => { 73 | assert.isTrue(await app.hasInitialized(), 'should be initialized') 74 | }) 75 | 76 | it('is still not petrified', async () => { 77 | assert.isFalse(await app.isPetrified(), 'should not be petrified') 78 | }) 79 | 80 | it('has correct initialization block', async () => { 81 | assert.equal(await app.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') 82 | }) 83 | 84 | it('throws on reinitialization', async () => { 85 | await assertRevert(app.initialize()) 86 | }) 87 | 88 | it('should still not be usable without a kernel', async () => { 89 | assert.equal(await app.getKernel(), ZERO_ADDRESS, 'app should still be missing kernel reference') 90 | 91 | assert.isFalse(await app.canPerform(permissionsRoot, FAKE_ROLE, [])) 92 | }) 93 | 94 | context('> Set kernel', () => { 95 | let acl, kernel 96 | 97 | beforeEach(async () => { 98 | const kernelProxy = await KernelProxy.new(kernelBase.address) 99 | kernel = await Kernel.at(kernelProxy.address) 100 | await kernel.initialize(aclBase.address, permissionsRoot) 101 | acl = await ACL.at(await kernel.acl()) 102 | 103 | await app.setKernelOnMock(kernel.address) 104 | }) 105 | 106 | it('should not be usable if no permission is granted', async () => { 107 | assert.isFalse(await app.canPerform(permissionsRoot, FAKE_ROLE, [])) 108 | }) 109 | 110 | it('should be usable after initialization, setting a kernel, and setting a permission', async () => { 111 | // Setup permissions 112 | await acl.createPermission(permissionsRoot, app.address, FAKE_ROLE, permissionsRoot) 113 | 114 | assert.isTrue(await app.canPerform(permissionsRoot, FAKE_ROLE, [])) 115 | }) 116 | }) 117 | }) 118 | }) 119 | }) 120 | -------------------------------------------------------------------------------- /test/contracts/forwarding/forwarding.js: -------------------------------------------------------------------------------- 1 | const { EMPTY_BYTES } = require('@aragon/contract-helpers-test') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | 4 | // Mocks 5 | const ForwarderMock = artifacts.require('ForwarderMock') 6 | const ForwarderPayableMock = artifacts.require('ForwarderPayableMock') 7 | const ForwarderWithContextMock = artifacts.require('ForwarderWithContextMock') 8 | const ForwarderWithContextPayableMock = artifacts.require('ForwarderWithContextPayableMock') 9 | 10 | const ForwarderType = { 11 | NOT_IMPLEMENTED: 0, 12 | NO_CONTEXT: 1, 13 | WITH_CONTEXT: 2, 14 | } 15 | 16 | contract('Forwarders', () => { 17 | context('IForwarder', () => { 18 | let forwarder 19 | 20 | beforeEach(async () => { 21 | forwarder = await ForwarderMock.new() 22 | }) 23 | 24 | it('is a forwarder', async () => { 25 | assert.isTrue(await forwarder.isForwarder(), 'should be a forwarder') 26 | }) 27 | 28 | it('reports correct forwarder type', async () => { 29 | assert.equal(await forwarder.forwarderType(), ForwarderType.NO_CONTEXT, 'should report correct forwarding type') 30 | }) 31 | 32 | it('can forward', async () => { 33 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES)) 34 | }) 35 | 36 | it('cannot forward with ETH payment', async () => { 37 | // Override the contract ABI to let us attempt sending value into this non-payable forwarder 38 | const payableForwarder = await ForwarderPayableMock.at(forwarder.address) 39 | await assertRevert(payableForwarder.forward(EMPTY_BYTES, { value: 1 })) 40 | }) 41 | }) 42 | 43 | context('IForwarderPayable', () => { 44 | let forwarder 45 | 46 | beforeEach(async () => { 47 | forwarder = await ForwarderPayableMock.new() 48 | }) 49 | 50 | it('is a forwarder', async () => { 51 | assert.isTrue(await forwarder.isForwarder(), 'should be a forwarder') 52 | }) 53 | 54 | it('reports correct forwarder type', async () => { 55 | assert.equal(await forwarder.forwarderType(), ForwarderType.NO_CONTEXT, 'should report correct forwarding type') 56 | }) 57 | 58 | it('can forward', async () => { 59 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES)) 60 | }) 61 | 62 | it('can forward with ETH payment', async () => { 63 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES, { value: 1 })) 64 | }) 65 | }) 66 | 67 | context('IForwarderWithContext', () => { 68 | let forwarder 69 | 70 | beforeEach(async () => { 71 | forwarder = await ForwarderWithContextMock.new() 72 | }) 73 | 74 | it('is a forwarder', async () => { 75 | assert.isTrue(await forwarder.isForwarder(), 'should be a forwarder') 76 | }) 77 | 78 | it('reports correct forwarder type', async () => { 79 | assert.equal(await forwarder.forwarderType(), ForwarderType.WITH_CONTEXT, 'should report correct forwarding type') 80 | }) 81 | 82 | it('can forward', async () => { 83 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES, EMPTY_BYTES)) 84 | }) 85 | 86 | it('cannot forward with ETH payment', async () => { 87 | // Override the contract ABI to let us attempt sending value into this non-payable forwarder 88 | const payableForwarder = await ForwarderWithContextPayableMock.at(forwarder.address) 89 | await assertRevert(payableForwarder.forward(EMPTY_BYTES, EMPTY_BYTES, { value: 1 })) 90 | }) 91 | }) 92 | 93 | context('IForwarderWithContextPayable', () => { 94 | let forwarder 95 | 96 | beforeEach(async () => { 97 | forwarder = await ForwarderWithContextPayableMock.new() 98 | }) 99 | 100 | it('is a forwarder', async () => { 101 | assert.isTrue(await forwarder.isForwarder(), 'should be a forwarder') 102 | }) 103 | 104 | it('reports correct forwarder type', async () => { 105 | assert.equal(await forwarder.forwarderType(), ForwarderType.WITH_CONTEXT, 'should report correct forwarding type') 106 | }) 107 | 108 | it('can forward', async () => { 109 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES, EMPTY_BYTES)) 110 | }) 111 | 112 | it('can forward with ETH payment', async () => { 113 | assert.doesNotThrow(async () => await forwarder.forward(EMPTY_BYTES, EMPTY_BYTES, { value: 1 })) 114 | }) 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/token/TokenMock.sol: -------------------------------------------------------------------------------- 1 | // Modified from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol 2 | 3 | pragma solidity 0.4.24; 4 | 5 | import "../../../../lib/math/SafeMath.sol"; 6 | 7 | 8 | contract TokenMock { 9 | using SafeMath for uint256; 10 | mapping (address => uint256) private balances; 11 | mapping (address => mapping (address => uint256)) private allowed; 12 | uint256 private totalSupply_; 13 | bool private allowTransfer_; 14 | 15 | event Approval(address indexed owner, address indexed spender, uint256 value); 16 | event Transfer(address indexed from, address indexed to, uint256 value); 17 | 18 | // Allow us to set the inital balance for an account on construction 19 | constructor(address initialAccount, uint256 initialBalance) public { 20 | balances[initialAccount] = initialBalance; 21 | totalSupply_ = initialBalance; 22 | allowTransfer_ = true; 23 | } 24 | 25 | function totalSupply() public view returns (uint256) { return totalSupply_; } 26 | 27 | /** 28 | * @dev Gets the balance of the specified address. 29 | * @param _owner The address to query the the balance of. 30 | * @return An uint256 representing the amount owned by the passed address. 31 | */ 32 | function balanceOf(address _owner) public view returns (uint256) { 33 | return balances[_owner]; 34 | } 35 | 36 | /** 37 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 38 | * @param _owner address The address which owns the funds. 39 | * @param _spender address The address which will spend the funds. 40 | * @return A uint256 specifying the amount of tokens still available for the spender. 41 | */ 42 | function allowance(address _owner, address _spender) public view returns (uint256) { 43 | return allowed[_owner][_spender]; 44 | } 45 | 46 | /** 47 | * @dev Set whether the token is transferable or not 48 | * @param _allowTransfer Should token be transferable 49 | */ 50 | function setAllowTransfer(bool _allowTransfer) public { 51 | allowTransfer_ = _allowTransfer; 52 | } 53 | 54 | /** 55 | * @dev Transfer token for a specified address 56 | * @param _to The address to transfer to. 57 | * @param _value The amount to be transferred. 58 | */ 59 | function transfer(address _to, uint256 _value) public returns (bool) { 60 | require(allowTransfer_); 61 | require(_value <= balances[msg.sender]); 62 | require(_to != address(0)); 63 | 64 | balances[msg.sender] = balances[msg.sender].sub(_value); 65 | balances[_to] = balances[_to].add(_value); 66 | emit Transfer(msg.sender, _to, _value); 67 | return true; 68 | } 69 | 70 | /** 71 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 72 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 73 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 74 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 75 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 76 | * @param _spender The address which will spend the funds. 77 | * @param _value The amount of tokens to be spent. 78 | */ 79 | function approve(address _spender, uint256 _value) public returns (bool) { 80 | // Assume we want to protect for the race condition 81 | require(allowed[msg.sender][_spender] == 0); 82 | 83 | allowed[msg.sender][_spender] = _value; 84 | emit Approval(msg.sender, _spender, _value); 85 | return true; 86 | } 87 | 88 | /** 89 | * @dev Transfer tokens from one address to another 90 | * @param _from address The address which you want to send tokens from 91 | * @param _to address The address which you want to transfer to 92 | * @param _value uint256 the amount of tokens to be transferred 93 | */ 94 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 95 | require(allowTransfer_); 96 | require(_value <= balances[_from]); 97 | require(_value <= allowed[_from][msg.sender]); 98 | require(_to != address(0)); 99 | 100 | balances[_from] = balances[_from].sub(_value); 101 | balances[_to] = balances[_to].add(_value); 102 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 103 | emit Transfer(_from, _to, _value); 104 | return true; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/token/TokenReturnMissingMock.sol: -------------------------------------------------------------------------------- 1 | // Non-standards compliant token that is missing return values for 2 | // `transfer()`, `transferFrom()`, and `approve(). 3 | // Modified from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol 4 | 5 | pragma solidity 0.4.24; 6 | 7 | import "../../../../lib/math/SafeMath.sol"; 8 | 9 | 10 | contract TokenReturnMissingMock { 11 | using SafeMath for uint256; 12 | mapping (address => uint256) private balances; 13 | mapping (address => mapping (address => uint256)) private allowed; 14 | uint256 private totalSupply_; 15 | bool private allowTransfer_; 16 | 17 | event Approval(address indexed owner, address indexed spender, uint256 value); 18 | event Transfer(address indexed from, address indexed to, uint256 value); 19 | 20 | // Allow us to set the inital balance for an account on construction 21 | constructor(address initialAccount, uint256 initialBalance) public { 22 | balances[initialAccount] = initialBalance; 23 | totalSupply_ = initialBalance; 24 | allowTransfer_ = true; 25 | } 26 | 27 | function totalSupply() public view returns (uint256) { return totalSupply_; } 28 | 29 | /** 30 | * @dev Gets the balance of the specified address. 31 | * @param _owner The address to query the the balance of. 32 | * @return An uint256 representing the amount owned by the passed address. 33 | */ 34 | function balanceOf(address _owner) public view returns (uint256) { 35 | return balances[_owner]; 36 | } 37 | 38 | /** 39 | * @dev Set whether the token is transferable or not 40 | * @param _allowTransfer Should token be transferable 41 | */ 42 | function setAllowTransfer(bool _allowTransfer) public { 43 | allowTransfer_ = _allowTransfer; 44 | } 45 | 46 | /** 47 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 48 | * @param _owner address The address which owns the funds. 49 | * @param _spender address The address which will spend the funds. 50 | * @return A uint256 specifying the amount of tokens still available for the spender. 51 | */ 52 | function allowance(address _owner, address _spender) public view returns (uint256) { 53 | return allowed[_owner][_spender]; 54 | } 55 | 56 | /** 57 | * @dev Transfer token for a specified address 58 | * @param _to The address to transfer to. 59 | * @param _value The amount to be transferred. 60 | */ 61 | function transfer(address _to, uint256 _value) public { 62 | require(allowTransfer_); 63 | require(_value <= balances[msg.sender]); 64 | require(_to != address(0)); 65 | 66 | balances[msg.sender] = balances[msg.sender].sub(_value); 67 | balances[_to] = balances[_to].add(_value); 68 | emit Transfer(msg.sender, _to, _value); 69 | } 70 | 71 | /** 72 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 73 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 74 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 75 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 76 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 77 | * @param _spender The address which will spend the funds. 78 | * @param _value The amount of tokens to be spent. 79 | */ 80 | function approve(address _spender, uint256 _value) public { 81 | // Assume we want to protect for the race condition 82 | require(allowed[msg.sender][_spender] == 0); 83 | 84 | allowed[msg.sender][_spender] = _value; 85 | emit Approval(msg.sender, _spender, _value); 86 | } 87 | 88 | /** 89 | * @dev Transfer tokens from one address to another 90 | * @param _from address The address which you want to send tokens from 91 | * @param _to address The address which you want to transfer to 92 | * @param _value uint256 the amount of tokens to be transferred 93 | */ 94 | function transferFrom(address _from, address _to, uint256 _value) public { 95 | require(allowTransfer_); 96 | require(_value <= balances[_from]); 97 | require(_value <= allowed[_from][msg.sender]); 98 | require(_to != address(0)); 99 | 100 | balances[_from] = balances[_from].sub(_value); 101 | balances[_to] = balances[_to].add(_value); 102 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 103 | emit Transfer(_from, _to, _value); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /contracts/test/mocks/lib/token/TokenReturnFalseMock.sol: -------------------------------------------------------------------------------- 1 | // Standards compliant token that is returns false instead of reverting for 2 | // `transfer()`, `transferFrom()`, and `approve(). 3 | // Modified from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol 4 | 5 | pragma solidity 0.4.24; 6 | 7 | 8 | contract TokenReturnFalseMock { 9 | mapping (address => uint256) private balances; 10 | mapping (address => mapping (address => uint256)) private allowed; 11 | uint256 private totalSupply_; 12 | bool private allowTransfer_; 13 | 14 | event Approval(address indexed owner, address indexed spender, uint256 value); 15 | event Transfer(address indexed from, address indexed to, uint256 value); 16 | 17 | // Allow us to set the inital balance for an account on construction 18 | constructor(address initialAccount, uint256 initialBalance) public { 19 | balances[initialAccount] = initialBalance; 20 | totalSupply_ = initialBalance; 21 | allowTransfer_ = true; 22 | } 23 | 24 | function totalSupply() public view returns (uint256) { return totalSupply_; } 25 | 26 | /** 27 | * @dev Gets the balance of the specified address. 28 | * @param _owner The address to query the the balance of. 29 | * @return An uint256 representing the amount owned by the passed address. 30 | */ 31 | function balanceOf(address _owner) public view returns (uint256) { 32 | return balances[_owner]; 33 | } 34 | 35 | /** 36 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 37 | * @param _owner address The address which owns the funds. 38 | * @param _spender address The address which will spend the funds. 39 | * @return A uint256 specifying the amount of tokens still available for the spender. 40 | */ 41 | function allowance(address _owner, address _spender) public view returns (uint256) { 42 | return allowed[_owner][_spender]; 43 | } 44 | 45 | /** 46 | * @dev Set whether the token is transferable or not 47 | * @param _allowTransfer Should token be transferable 48 | */ 49 | function setAllowTransfer(bool _allowTransfer) public { 50 | allowTransfer_ = _allowTransfer; 51 | } 52 | 53 | /** 54 | * @dev Transfer token for a specified address 55 | * @param _to The address to transfer to. 56 | * @param _value The amount to be transferred. 57 | */ 58 | function transfer(address _to, uint256 _value) public returns (bool) { 59 | if (!allowTransfer_ || _to == address(0) || _value > balances[msg.sender]) { 60 | return false; 61 | } 62 | 63 | balances[msg.sender] = balances[msg.sender] - _value; 64 | balances[_to] = balances[_to] + _value; 65 | emit Transfer(msg.sender, _to, _value); 66 | return true; 67 | } 68 | 69 | /** 70 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 71 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 72 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 73 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 74 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 75 | * @param _spender The address which will spend the funds. 76 | * @param _value The amount of tokens to be spent. 77 | */ 78 | function approve(address _spender, uint256 _value) public returns (bool) { 79 | // Assume we want to protect for the race condition 80 | if (allowed[msg.sender][_spender] != 0) { 81 | return false; 82 | } 83 | 84 | allowed[msg.sender][_spender] = _value; 85 | emit Approval(msg.sender, _spender, _value); 86 | return true; 87 | } 88 | 89 | /** 90 | * @dev Transfer tokens from one address to another 91 | * @param _from address The address which you want to send tokens from 92 | * @param _to address The address which you want to transfer to 93 | * @param _value uint256 the amount of tokens to be transferred 94 | */ 95 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 96 | if (!allowTransfer_ || 97 | _to == address(0) || 98 | _value > balances[_from] || 99 | _value > allowed[_from][msg.sender] 100 | ) { 101 | return false; 102 | } 103 | 104 | balances[_from] = balances[_from] - _value; 105 | balances[_to] = balances[_to] + _value; 106 | allowed[_from][msg.sender] = allowed[_from][msg.sender] - _value; 107 | emit Transfer(_from, _to, _value); 108 | return true; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/contracts/apps/app_funds.js: -------------------------------------------------------------------------------- 1 | const { hash } = require('eth-ens-namehash') 2 | const { bn, onlyIf } = require('@aragon/contract-helpers-test') 3 | const { assertRevert, assertBn } = require('@aragon/contract-helpers-test/src/asserts') 4 | 5 | const ACL = artifacts.require('ACL') 6 | const Kernel = artifacts.require('Kernel') 7 | const KernelProxy = artifacts.require('KernelProxy') 8 | const AppProxyUpgradeable = artifacts.require('AppProxyUpgradeable') 9 | const AppProxyPinned = artifacts.require('AppProxyPinned') 10 | 11 | // Mocks 12 | const AppStub = artifacts.require('AppStub') 13 | const UnsafeAppStub = artifacts.require('UnsafeAppStub') 14 | const AppStubDepositable = artifacts.require('AppStubDepositable') 15 | const UnsafeAppStubDepositable = artifacts.require('UnsafeAppStubDepositable') 16 | 17 | const APP_ID = hash('stub.aragonpm.test') 18 | const EMPTY_BYTES = '0x' 19 | const TX_BASE_GAS = 21000 20 | const SEND_ETH_GAS = TX_BASE_GAS + 9999 // <10k gas is the threshold for depositing 21 | 22 | contract('App funds', ([permissionsRoot]) => { 23 | let aclBase, kernelBase 24 | let APP_BASES_NAMESPACE 25 | 26 | before(async () => { 27 | kernelBase = await Kernel.new(true) // petrify immediately 28 | aclBase = await ACL.new() 29 | 30 | // Setup constants 31 | APP_BASES_NAMESPACE = await kernelBase.APP_BASES_NAMESPACE() 32 | }) 33 | 34 | const appBaseGroups = [ 35 | { 36 | base: AppStub, 37 | unsafeBase: UnsafeAppStub, 38 | }, 39 | { 40 | base: AppStubDepositable, 41 | unsafeBase: UnsafeAppStubDepositable, 42 | } 43 | ] 44 | for (const { base: appBaseType, unsafeBase: unsafeAppBaseType } of appBaseGroups) { 45 | context(`> ${appBaseType.contractName}`, () => { 46 | const onlyAppStubDepositable = onlyIf(() => appBaseType === AppStubDepositable) 47 | 48 | // Test the app itself and when it's behind the proxies to make sure their behaviours are the same 49 | const appProxyTypes = ['AppProxyUpgradeable', 'AppProxyPinned'] 50 | for (const appType of ['App', ...appProxyTypes]) { 51 | context(`> ${appType}`, () => { 52 | let appBase, app 53 | 54 | before(async () => { 55 | if (appProxyTypes.includes(appType)) { 56 | // We can reuse the same app base for the proxies 57 | appBase = await appBaseType.new() 58 | } 59 | }) 60 | 61 | beforeEach(async () => { 62 | const kernel = await Kernel.at((await KernelProxy.new(kernelBase.address)).address) 63 | await kernel.initialize(aclBase.address, permissionsRoot) 64 | 65 | if (appType === 'App') { 66 | // Use the unsafe version to use directly without a proxy 67 | app = await unsafeAppBaseType.new(kernel.address) 68 | } else { 69 | // Install app 70 | const acl = await ACL.at(await kernel.acl()) 71 | const APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE() 72 | await acl.createPermission(permissionsRoot, kernel.address, APP_MANAGER_ROLE, permissionsRoot) 73 | await kernel.setApp(APP_BASES_NAMESPACE, APP_ID, appBase.address) 74 | 75 | let appProxy 76 | if (appType === 'AppProxyUpgradeable') { 77 | appProxy = await AppProxyUpgradeable.new(kernel.address, APP_ID, EMPTY_BYTES) 78 | } else if (appType === 'AppProxyPinned') { 79 | appProxy = await AppProxyPinned.new(kernel.address, APP_ID, EMPTY_BYTES) 80 | } 81 | 82 | app = await appBaseType.at(appProxy.address) 83 | } 84 | 85 | await app.initialize() 86 | }) 87 | 88 | it('cannot receive ETH', async () => { 89 | assert.isTrue(await app.hasInitialized(), 'should have been initialized') 90 | 91 | await assertRevert(app.sendTransaction({ value: 1, gas: SEND_ETH_GAS })) 92 | }) 93 | 94 | onlyAppStubDepositable(() => { 95 | it('does not have depositing enabled by default', async () => { 96 | assert.isTrue(await app.hasInitialized(), 'should have been initialized') 97 | assert.isFalse(await app.isDepositable(), 'should not be depositable') 98 | }) 99 | 100 | it('can receive ETH after being set to depositable', async () => { 101 | const amount = bn(1) 102 | const initialBalance = bn(await web3.eth.getBalance(app.address)) 103 | 104 | await app.enableDeposits() 105 | assert.isTrue(await app.isDepositable(), 'should be depositable') 106 | 107 | await app.sendTransaction({ value: 1, gas: SEND_ETH_GAS }) 108 | assertBn(bn(await web3.eth.getBalance(app.address)), initialBalance.add(amount)) 109 | }) 110 | }) 111 | }) 112 | } 113 | }) 114 | } 115 | }) 116 | -------------------------------------------------------------------------------- /contracts/evmscript/EVMScriptRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../apps/AragonApp.sol"; 4 | import "./ScriptHelpers.sol"; 5 | import "./IEVMScriptExecutor.sol"; 6 | import "./IEVMScriptRegistry.sol"; 7 | 8 | 9 | /* solium-disable function-order */ 10 | // Allow public initialize() to be first 11 | contract EVMScriptRegistry is IEVMScriptRegistry, EVMScriptRegistryConstants, AragonApp { 12 | using ScriptHelpers for bytes; 13 | 14 | /* Hardcoded constants to save gas 15 | bytes32 public constant REGISTRY_ADD_EXECUTOR_ROLE = keccak256("REGISTRY_ADD_EXECUTOR_ROLE"); 16 | bytes32 public constant REGISTRY_MANAGER_ROLE = keccak256("REGISTRY_MANAGER_ROLE"); 17 | */ 18 | bytes32 public constant REGISTRY_ADD_EXECUTOR_ROLE = 0xc4e90f38eea8c4212a009ca7b8947943ba4d4a58d19b683417f65291d1cd9ed2; 19 | // WARN: Manager can censor all votes and the like happening in an org 20 | bytes32 public constant REGISTRY_MANAGER_ROLE = 0xf7a450ef335e1892cb42c8ca72e7242359d7711924b75db5717410da3f614aa3; 21 | 22 | uint256 internal constant SCRIPT_START_LOCATION = 4; 23 | 24 | string private constant ERROR_INEXISTENT_EXECUTOR = "EVMREG_INEXISTENT_EXECUTOR"; 25 | string private constant ERROR_EXECUTOR_ENABLED = "EVMREG_EXECUTOR_ENABLED"; 26 | string private constant ERROR_EXECUTOR_DISABLED = "EVMREG_EXECUTOR_DISABLED"; 27 | string private constant ERROR_SCRIPT_LENGTH_TOO_SHORT = "EVMREG_SCRIPT_LENGTH_TOO_SHORT"; 28 | 29 | struct ExecutorEntry { 30 | IEVMScriptExecutor executor; 31 | bool enabled; 32 | } 33 | 34 | uint256 private executorsNextIndex; 35 | mapping (uint256 => ExecutorEntry) public executors; 36 | 37 | event EnableExecutor(uint256 indexed executorId, address indexed executorAddress); 38 | event DisableExecutor(uint256 indexed executorId, address indexed executorAddress); 39 | 40 | modifier executorExists(uint256 _executorId) { 41 | require(_executorId > 0 && _executorId < executorsNextIndex, ERROR_INEXISTENT_EXECUTOR); 42 | _; 43 | } 44 | 45 | /** 46 | * @notice Initialize the registry 47 | */ 48 | function initialize() public onlyInit { 49 | initialized(); 50 | // Create empty record to begin executor IDs at 1 51 | executorsNextIndex = 1; 52 | } 53 | 54 | /** 55 | * @notice Add a new script executor with address `_executor` to the registry 56 | * @param _executor Address of the IEVMScriptExecutor that will be added to the registry 57 | * @return id Identifier of the executor in the registry 58 | */ 59 | function addScriptExecutor(IEVMScriptExecutor _executor) external auth(REGISTRY_ADD_EXECUTOR_ROLE) returns (uint256 id) { 60 | uint256 executorId = executorsNextIndex++; 61 | executors[executorId] = ExecutorEntry(_executor, true); 62 | emit EnableExecutor(executorId, _executor); 63 | return executorId; 64 | } 65 | 66 | /** 67 | * @notice Disable script executor with ID `_executorId` 68 | * @param _executorId Identifier of the executor in the registry 69 | */ 70 | function disableScriptExecutor(uint256 _executorId) 71 | external 72 | authP(REGISTRY_MANAGER_ROLE, arr(_executorId)) 73 | { 74 | // Note that we don't need to check for an executor's existence in this case, as only 75 | // existing executors can be enabled 76 | ExecutorEntry storage executorEntry = executors[_executorId]; 77 | require(executorEntry.enabled, ERROR_EXECUTOR_DISABLED); 78 | executorEntry.enabled = false; 79 | emit DisableExecutor(_executorId, executorEntry.executor); 80 | } 81 | 82 | /** 83 | * @notice Enable script executor with ID `_executorId` 84 | * @param _executorId Identifier of the executor in the registry 85 | */ 86 | function enableScriptExecutor(uint256 _executorId) 87 | external 88 | authP(REGISTRY_MANAGER_ROLE, arr(_executorId)) 89 | executorExists(_executorId) 90 | { 91 | ExecutorEntry storage executorEntry = executors[_executorId]; 92 | require(!executorEntry.enabled, ERROR_EXECUTOR_ENABLED); 93 | executorEntry.enabled = true; 94 | emit EnableExecutor(_executorId, executorEntry.executor); 95 | } 96 | 97 | /** 98 | * @dev Get the script executor that can execute a particular script based on its first 4 bytes 99 | * @param _script EVMScript being inspected 100 | */ 101 | function getScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) { 102 | require(_script.length >= SCRIPT_START_LOCATION, ERROR_SCRIPT_LENGTH_TOO_SHORT); 103 | uint256 id = _script.getSpecId(); 104 | 105 | // Note that we don't need to check for an executor's existence in this case, as only 106 | // existing executors can be enabled 107 | ExecutorEntry storage entry = executors[id]; 108 | return entry.enabled ? entry.executor : IEVMScriptExecutor(0); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /contracts/evmscript/executors/CallsScript.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | // Inspired by https://github.com/reverendus/tx-manager 4 | 5 | import "../ScriptHelpers.sol"; 6 | import "./BaseEVMScriptExecutor.sol"; 7 | 8 | 9 | contract CallsScript is BaseEVMScriptExecutor { 10 | using ScriptHelpers for bytes; 11 | 12 | /* Hardcoded constants to save gas 13 | bytes32 internal constant EXECUTOR_TYPE = keccak256("CALLS_SCRIPT"); 14 | */ 15 | bytes32 internal constant EXECUTOR_TYPE = 0x2dc858a00f3e417be1394b87c07158e989ec681ce8cc68a9093680ac1a870302; 16 | 17 | string private constant ERROR_BLACKLISTED_CALL = "EVMCALLS_BLACKLISTED_CALL"; 18 | string private constant ERROR_INVALID_LENGTH = "EVMCALLS_INVALID_LENGTH"; 19 | 20 | /* This is manually crafted in assembly 21 | string private constant ERROR_CALL_REVERTED = "EVMCALLS_CALL_REVERTED"; 22 | */ 23 | 24 | event LogScriptCall(address indexed sender, address indexed src, address indexed dst); 25 | 26 | /** 27 | * @notice Executes a number of call scripts 28 | * @param _script [ specId (uint32) ] many calls with this structure -> 29 | * [ to (address: 20 bytes) ] [ calldataLength (uint32: 4 bytes) ] [ calldata (calldataLength bytes) ] 30 | * @param _blacklist Addresses the script cannot call to, or will revert. 31 | * @return Always returns empty byte array 32 | */ 33 | function execScript(bytes _script, bytes, address[] _blacklist) external isInitialized returns (bytes) { 34 | uint256 location = SCRIPT_START_LOCATION; // first 32 bits are spec id 35 | while (location < _script.length) { 36 | // Check there's at least address + calldataLength available 37 | require(_script.length - location >= 0x18, ERROR_INVALID_LENGTH); 38 | 39 | address contractAddress = _script.addressAt(location); 40 | // Check address being called is not blacklist 41 | for (uint256 i = 0; i < _blacklist.length; i++) { 42 | require(contractAddress != _blacklist[i], ERROR_BLACKLISTED_CALL); 43 | } 44 | 45 | // logged before execution to ensure event ordering in receipt 46 | // if failed entire execution is reverted regardless 47 | emit LogScriptCall(msg.sender, address(this), contractAddress); 48 | 49 | uint256 calldataLength = uint256(_script.uint32At(location + 0x14)); 50 | uint256 startOffset = location + 0x14 + 0x04; 51 | uint256 calldataStart = _script.locationOf(startOffset); 52 | 53 | // compute end of script / next location 54 | location = startOffset + calldataLength; 55 | require(location <= _script.length, ERROR_INVALID_LENGTH); 56 | 57 | bool success; 58 | assembly { 59 | success := call( 60 | sub(gas, 5000), // forward gas left - 5000 61 | contractAddress, // address 62 | 0, // no value 63 | calldataStart, // calldata start 64 | calldataLength, // calldata length 65 | 0, // don't write output 66 | 0 // don't write output 67 | ) 68 | 69 | switch success 70 | case 0 { 71 | let ptr := mload(0x40) 72 | 73 | switch returndatasize 74 | case 0 { 75 | // No error data was returned, revert with "EVMCALLS_CALL_REVERTED" 76 | // See remix: doing a `revert("EVMCALLS_CALL_REVERTED")` always results in 77 | // this memory layout 78 | mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // error identifier 79 | mstore(add(ptr, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset 80 | mstore(add(ptr, 0x24), 0x0000000000000000000000000000000000000000000000000000000000000016) // reason length 81 | mstore(add(ptr, 0x44), 0x45564d43414c4c535f43414c4c5f524556455254454400000000000000000000) // reason 82 | 83 | revert(ptr, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error) 84 | } 85 | default { 86 | // Forward the full error data 87 | returndatacopy(ptr, 0, returndatasize) 88 | revert(ptr, returndatasize) 89 | } 90 | } 91 | default { } 92 | } 93 | } 94 | // No need to allocate empty bytes for the return as this can only be called via an delegatecall 95 | // (due to the isInitialized modifier) 96 | } 97 | 98 | function executorType() external pure returns (bytes32) { 99 | return EXECUTOR_TYPE; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /contracts/evmscript/EVMScriptRunner.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./IEVMScriptExecutor.sol"; 8 | import "./IEVMScriptRegistry.sol"; 9 | 10 | import "../apps/AppStorage.sol"; 11 | import "../kernel/KernelConstants.sol"; 12 | import "../common/Initializable.sol"; 13 | 14 | 15 | contract EVMScriptRunner is AppStorage, Initializable, EVMScriptRegistryConstants, KernelNamespaceConstants { 16 | string private constant ERROR_EXECUTOR_UNAVAILABLE = "EVMRUN_EXECUTOR_UNAVAILABLE"; 17 | string private constant ERROR_PROTECTED_STATE_MODIFIED = "EVMRUN_PROTECTED_STATE_MODIFIED"; 18 | 19 | /* This is manually crafted in assembly 20 | string private constant ERROR_EXECUTOR_INVALID_RETURN = "EVMRUN_EXECUTOR_INVALID_RETURN"; 21 | */ 22 | 23 | event ScriptResult(address indexed executor, bytes script, bytes input, bytes returnData); 24 | 25 | function getEVMScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) { 26 | return IEVMScriptExecutor(getEVMScriptRegistry().getScriptExecutor(_script)); 27 | } 28 | 29 | function getEVMScriptRegistry() public view returns (IEVMScriptRegistry) { 30 | address registryAddr = kernel().getApp(KERNEL_APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID); 31 | return IEVMScriptRegistry(registryAddr); 32 | } 33 | 34 | function runScript(bytes _script, bytes _input, address[] _blacklist) 35 | internal 36 | isInitialized 37 | protectState 38 | returns (bytes) 39 | { 40 | IEVMScriptExecutor executor = getEVMScriptExecutor(_script); 41 | require(address(executor) != address(0), ERROR_EXECUTOR_UNAVAILABLE); 42 | 43 | bytes4 sig = executor.execScript.selector; 44 | bytes memory data = abi.encodeWithSelector(sig, _script, _input, _blacklist); 45 | 46 | bytes memory output; 47 | assembly { 48 | let success := delegatecall( 49 | gas, // forward all gas 50 | executor, // address 51 | add(data, 0x20), // calldata start 52 | mload(data), // calldata length 53 | 0, // don't write output (we'll handle this ourselves) 54 | 0 // don't write output 55 | ) 56 | 57 | output := mload(0x40) // free mem ptr get 58 | 59 | switch success 60 | case 0 { 61 | // If the call errored, forward its full error data 62 | returndatacopy(output, 0, returndatasize) 63 | revert(output, returndatasize) 64 | } 65 | default { 66 | switch gt(returndatasize, 0x3f) 67 | case 0 { 68 | // Need at least 0x40 bytes returned for properly ABI-encoded bytes values, 69 | // revert with "EVMRUN_EXECUTOR_INVALID_RETURN" 70 | // See remix: doing a `revert("EVMRUN_EXECUTOR_INVALID_RETURN")` always results in 71 | // this memory layout 72 | mstore(output, 0x08c379a000000000000000000000000000000000000000000000000000000000) // error identifier 73 | mstore(add(output, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset 74 | mstore(add(output, 0x24), 0x000000000000000000000000000000000000000000000000000000000000001e) // reason length 75 | mstore(add(output, 0x44), 0x45564d52554e5f4558454355544f525f494e56414c49445f52455455524e0000) // reason 76 | 77 | revert(output, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error) 78 | } 79 | default { 80 | // Copy result 81 | // 82 | // Needs to perform an ABI decode for the expected `bytes` return type of 83 | // `executor.execScript()` as solidity will automatically ABI encode the returned bytes as: 84 | // [ position of the first dynamic length return value = 0x20 (32 bytes) ] 85 | // [ output length (32 bytes) ] 86 | // [ output content (N bytes) ] 87 | // 88 | // Perform the ABI decode by ignoring the first 32 bytes of the return data 89 | let copysize := sub(returndatasize, 0x20) 90 | returndatacopy(output, 0x20, copysize) 91 | 92 | mstore(0x40, add(output, copysize)) // free mem ptr set 93 | } 94 | } 95 | } 96 | 97 | emit ScriptResult(address(executor), _script, _input, output); 98 | 99 | return output; 100 | } 101 | 102 | modifier protectState { 103 | address preKernel = address(kernel()); 104 | bytes32 preAppId = appId(); 105 | _; // exec 106 | require(address(kernel()) == preKernel, ERROR_PROTECTED_STATE_MODIFIED); 107 | require(appId() == preAppId, ERROR_PROTECTED_STATE_MODIFIED); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /test/contracts/common/unstructured_storage.js: -------------------------------------------------------------------------------- 1 | const Kernel = artifacts.require('Kernel') 2 | const AppStub = artifacts.require('AppStub') 3 | 4 | // Mocks 5 | const AppStorageMock = artifacts.require('AppStorageMock') 6 | const AppProxyPinnedStorageMock = artifacts.require('AppProxyPinnedStorageMock') 7 | const DepositableStorageMock = artifacts.require('DepositableStorageMock') 8 | const InitializableStorageMock = artifacts.require('InitializableStorageMock') 9 | const KernelPinnedStorageMock = artifacts.require('KernelPinnedStorageMock') 10 | const ReentrancyGuardMock = artifacts.require('ReentrancyGuardMock') 11 | 12 | contract('Unstructured storage', () => { 13 | context('> AppStorage', () => { 14 | let appStorage 15 | 16 | beforeEach(async () => { 17 | appStorage = await AppStorageMock.new() 18 | }) 19 | 20 | it('tests Kernel storage', async () => { 21 | const kernel = await Kernel.new(true) 22 | await appStorage.setKernelExt(kernel.address) 23 | // checks 24 | assert.equal( 25 | await web3.eth.getStorageAt(appStorage.address, (await appStorage.getKernelPosition())), 26 | (await appStorage.kernel()).toString().toLowerCase(), 27 | 'Kernel should match' 28 | ) 29 | assert.equal( 30 | await web3.eth.getStorageAt(appStorage.address, (await appStorage.getKernelPosition())), 31 | kernel.address.toLowerCase(), 32 | 'Kernel original value should match' 33 | ) 34 | }) 35 | 36 | it('tests appID storage', async () => { 37 | const appId = '0x1234000000000000000000000000000000000000000000000000000000000000' 38 | await appStorage.setAppIdExt(appId) 39 | // checks 40 | assert.equal( 41 | await web3.eth.getStorageAt(appStorage.address, (await appStorage.getAppIdPosition())), 42 | (await appStorage.appId()).toString(), 43 | 'appId should match' 44 | ) 45 | assert.equal( 46 | await web3.eth.getStorageAt(appStorage.address, (await appStorage.getAppIdPosition())), 47 | appId, 48 | 'appId original value should match' 49 | ) 50 | }) 51 | }) 52 | 53 | context('> AppProxyPinned', () => { 54 | let appPinned 55 | beforeEach(async () => { 56 | // Set up AppStubPinnedStorage 57 | const fakeApp = await AppStub.new() 58 | const kernelMock = await KernelPinnedStorageMock.new(fakeApp.address) 59 | appPinned = await AppProxyPinnedStorageMock.new(kernelMock.address) 60 | }) 61 | 62 | it('tests pinnedCode storage', async () => { 63 | const pinnedCode = '0x1200000000000000000000000000000000005678' 64 | await appPinned.setPinnedCodeExt(pinnedCode) 65 | // checks 66 | assert.equal( 67 | await web3.eth.getStorageAt(appPinned.address, (await appPinned.getPinnedCodePosition())), 68 | (await appPinned.pinnedCodeExt()).toString(), 69 | 'Pinned Code should match' 70 | ) 71 | assert.equal( 72 | await web3.eth.getStorageAt(appPinned.address, (await appPinned.getPinnedCodePosition())), 73 | pinnedCode, 74 | 'Pinned Code original value should match' 75 | ) 76 | }) 77 | }) 78 | 79 | context('> DepositableStorage', () => { 80 | let depositableMock 81 | 82 | beforeEach(async () => { 83 | depositableMock = await DepositableStorageMock.new() 84 | }) 85 | 86 | it('tests depositable', async () => { 87 | // set values 88 | await depositableMock.setDepositableExt(true) 89 | // checks 90 | assert.equal( 91 | await web3.eth.getStorageAt(depositableMock.address, (await depositableMock.getDepositablePosition())), 92 | true, 93 | 'Depositable should match' 94 | ) 95 | }) 96 | }) 97 | 98 | context('> Initializable', () => { 99 | let initializableMock 100 | 101 | beforeEach(async () => { 102 | initializableMock = await InitializableStorageMock.new() 103 | }) 104 | 105 | it('tests init block', async () => { 106 | // set values 107 | await initializableMock.initialize() 108 | const blockNumber = await web3.eth.getBlockNumber() 109 | // checks 110 | assert.equal( 111 | parseInt( 112 | await web3.eth.getStorageAt( 113 | initializableMock.address, 114 | (await initializableMock.getInitializationBlockPosition()) 115 | ), 116 | 16 117 | ), 118 | blockNumber, 119 | 'Init block should match' 120 | ) 121 | assert.equal( 122 | parseInt( 123 | await web3.eth.getStorageAt( 124 | initializableMock.address, 125 | (await initializableMock.getInitializationBlockPosition()) 126 | ), 127 | 16 128 | ), 129 | (await initializableMock.getInitializationBlock()).toString(), 130 | 'Init block should match' 131 | ) 132 | }) 133 | }) 134 | 135 | context('> ReentrancyGuard', () => { 136 | let reentrancyGuardMock 137 | 138 | beforeEach(async () => { 139 | reentrancyGuardMock = await ReentrancyGuardMock.new() 140 | }) 141 | 142 | it('tests reentrancy mutex', async () => { 143 | // set values 144 | await reentrancyGuardMock.setReentrancyMutex(true) 145 | // checks 146 | assert.equal( 147 | await web3.eth.getStorageAt(reentrancyGuardMock.address, (await reentrancyGuardMock.getReentrancyMutexPosition())), 148 | true, 149 | 'Reentrancy mutex should match' 150 | ) 151 | }) 152 | }) 153 | }) 154 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to aragonOS 2 | 3 | :tada: Thank you for being interested in contributing to aragonOS! :tada: 4 | 5 | Feel welcome and read the following sections in order to know how to ask questions and how to work on something. 6 | 7 | There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into the project. 8 | 9 | All members of our community are expected to follow our [Code of Conduct](https://wiki.aragon.org/documentation/Code_of_Conduct/). Please make sure you are welcoming and friendly in all of our spaces. 10 | 11 | ## Project status 12 | 13 | aragonOS has been [deployed to Ethereum mainnet and other live networks](https://github.com/aragon/deployments). 14 | 15 | Outside of clear security issues or specific feature enhancements, we are usually not working to 16 | actively change the existing contracts. 17 | 18 | This is made even more difficult due to the difficult and sensitive process involved in launching changes in on-chain environments. 19 | 20 | ## Branching strategy 21 | 22 | You should treat [`master`](https://github.com/aragon/aragonOS/tree/master) as the main, audited branch. 23 | 24 | We use [`next`](https://github.com/aragon/aragonOS/tree/next) as the default branch to help expose upcoming changes for new versions that have not yet undergone security review. 25 | 26 | ## Your first contribution 27 | 28 | Unsure where to begin contributing to aragonOS? 29 | 30 | You can start with a [Good First Issue](https://github.com/aragon/aragonOS/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) 31 | 32 | > Good first issues are usually for small features, additional tests, spelling / grammar fixes, formatting changes, or other clean up. 33 | 34 | Start small, pick a subject you care about, are familiar with, or want to learn. 35 | 36 | If you're not already familiar with git or Github, here are a couple of friendly tutorials: [First Contributions](https://github.com/firstcontributions/first-contributions), [Open Source Guide](https://opensource.guide/), and [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 37 | 38 | ## How to file an issue or report a bug 39 | 40 | If you see a problem, you can report it in our [issue tracker](https://github.com/aragon/aragonOS/issues). 41 | 42 | Please take a quick look to see if the issue doesn't already exist before filing yours. 43 | 44 | Do your best to include as many details as needed in order for someone else to fix the problem and resolve the issue. 45 | 46 | #### If you find a security vulnerability, do NOT open an issue. Email security@aragon.org instead. 47 | 48 | In order to determine whether you are dealing with a security issue, ask yourself these two questions: 49 | 50 | - Can I access or steal something that's not mine, or access something I shouldn't have access to? 51 | - Can I disable something for other people? 52 | 53 | If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, please send a email. 54 | 55 | #### A [bug bounty program](https://wiki.aragon.org/dev/bug_bounty/) is available for rewarding contributors who find security vulnerabilities with payouts up to $50,000. 56 | 57 | ## Fixing issues 58 | 59 | 1. [Find an issue](https://github.com/aragon/aragonOS/issues) that you are interested in. 60 | - You may want to ask on the issue or in the [aragonOS Spectrum channel](https://spectrum.chat/aragon/aragonos) if anyone has already started working on the issue. 61 | 1. Fork and clone a local copy of the repository. 62 | 1. Make the appropriate changes for the issue you are trying to address or the feature that you want to add. 63 | - Make sure to add tests! 64 | 1. Push the changes to the remote repository. 65 | 1. Submit a pull request in Github, explaining any changes and further questions you may have. 66 | 1. Wait for the pull request to be reviewed. 67 | 1. Make changes to the pull request if the maintainer recommends them. 68 | 1. Celebrate your success after your pull request is merged! 69 | 70 | It's OK if your pull request is not perfect (no pull request is). 71 | The reviewer will be able to help you fix any problems and improve it! 72 | 73 | You can also edit a page directly through your browser by clicking the "EDIT" link in the top-right corner of any page and then clicking the pencil icon in the github copy of the page. 74 | 75 | ## Styleguide and development processes 76 | 77 | We generally follow [Solidity's style guide](https://solidity.readthedocs.io/en/v0.4.24/style-guide.html) and have set up [Ethlint](https://github.com/duaraghav8/Ethlint) to automatically lint the project. 78 | 79 | Due to the sensitive nature of Solidity, usually at least two reviewers are required before merging any pull request with code changes. 80 | 81 | ### Licensing 82 | 83 | aragonOS is generally meant to be used as a library by developers but includes core components that are not generally useful to extend. Any interfaces or contracts meant to be used by other developers are licensed as MIT and have their Solidity pragmas left unpinned. All other contracts are licensed as GPL-3 and are pinned to a specific Solidity version. 84 | 85 | ## Community 86 | 87 | If you need help, please reach out to Aragon core contributors and community members in the [aragonOS Spectrum channel](https://spectrum.chat/aragon/aragonos). We'd love to hear from you and know what you're working on! 88 | -------------------------------------------------------------------------------- /test/contracts/common/keccak_constants.js: -------------------------------------------------------------------------------- 1 | const getContract = name => artifacts.require(name) 2 | 3 | contract('Constants', () => { 4 | let keccakConstants 5 | 6 | before(async () => { 7 | keccakConstants = await getContract('KeccakConstants').new() 8 | }) 9 | 10 | it('checks kernel constants', async () => { 11 | const kernelConstants = await getContract('KernelConstantsMock').new() 12 | assert.equal(await kernelConstants.getKernelAppId(), await keccakConstants.KERNEL_APP_ID(), "kernel app id doesn't match") 13 | assert.equal(await kernelConstants.getDefaultACLAppId(), await keccakConstants.DEFAULT_ACL_APP_ID(), "default ACL id doesn't match") 14 | assert.equal(await kernelConstants.getDefaultVaultAppId(), await keccakConstants.DEFAULT_VAULT_APP_ID(), "default vault id doesn't match") 15 | assert.equal(await kernelConstants.getKernelCoreNamespace(), await keccakConstants.KERNEL_CORE_NAMESPACE(), "core namespace doesn't match") 16 | assert.equal(await kernelConstants.getKernelAppBasesNamespace(), await keccakConstants.KERNEL_APP_BASES_NAMESPACE(), "base namespace doesn't match") 17 | assert.equal(await kernelConstants.getKernelAppAddrNamespace(), await keccakConstants.KERNEL_APP_ADDR_NAMESPACE(), "app namespace doesn't match") 18 | 19 | const kernel = await getContract('Kernel').new(false) 20 | assert.equal(await kernel.APP_MANAGER_ROLE(), await keccakConstants.APP_MANAGER_ROLE(), "app manager role doesn't match") 21 | assert.equal(await kernel.KERNEL_APP_ID(), await keccakConstants.KERNEL_APP_ID(), "app id doesn't match") 22 | assert.equal(await kernel.DEFAULT_ACL_APP_ID(), await keccakConstants.DEFAULT_ACL_APP_ID(), "default acl id doesn't match") 23 | assert.equal(await kernel.CORE_NAMESPACE(), await keccakConstants.KERNEL_CORE_NAMESPACE(), "core namespace doesn't match") 24 | assert.equal(await kernel.APP_BASES_NAMESPACE(), await keccakConstants.KERNEL_APP_BASES_NAMESPACE(), "base namespace doesn't match") 25 | assert.equal(await kernel.APP_ADDR_NAMESPACE(), await keccakConstants.KERNEL_APP_ADDR_NAMESPACE(), "app namespace doesn't match") 26 | }) 27 | 28 | it('checks ACL constants', async () => { 29 | const acl = await getContract('ACL').new() 30 | 31 | assert.equal(await acl.CREATE_PERMISSIONS_ROLE(), await keccakConstants.CREATE_PERMISSIONS_ROLE(), "create permissions role doesn't match") 32 | assert.equal(await acl.EMPTY_PARAM_HASH(), await keccakConstants.EMPTY_PARAM_HASH(), "empty param hash doesn't match") 33 | }) 34 | 35 | it('checks EVM Script constants', async () => { 36 | const evmScriptConstants = await getContract('EVMScriptRegistryConstantsMock').new() 37 | 38 | assert.equal(await evmScriptConstants.getEVMScriptRegistryAppId(), await keccakConstants.EVMSCRIPT_REGISTRY_APP_ID(), "app id doesn't match") 39 | }) 40 | 41 | it('checks EVM Script executor types', async () => { 42 | const callsScriptExecutor = await getContract('CallsScript').new() 43 | 44 | assert.equal(await callsScriptExecutor.executorType(), await keccakConstants.EVMSCRIPT_EXECUTOR_CALLS_SCRIPT(), "callscript executor type doesn't match") 45 | }) 46 | 47 | it('checks EVMScriptRegistry constants', async () => { 48 | const evmScriptRegistry = await getContract('EVMScriptRegistry').new() 49 | 50 | assert.equal(await evmScriptRegistry.REGISTRY_ADD_EXECUTOR_ROLE(), await keccakConstants.REGISTRY_ADD_EXECUTOR_ROLE(), "registry add executor role doesn't match") 51 | assert.equal(await evmScriptRegistry.REGISTRY_MANAGER_ROLE(), await keccakConstants.REGISTRY_MANAGER_ROLE(), "registry manager role doesn't match") 52 | }) 53 | 54 | it('checks AppStorage unstructured storage constants', async () => { 55 | const appStorage = await getContract('AppStorageMock').new() 56 | 57 | assert.equal(await appStorage.getKernelPosition(), await keccakConstants.kernelPosition(), "kernelPosition doesn't match") 58 | assert.equal(await appStorage.getAppIdPosition(), await keccakConstants.appIdPosition(), "appIdPosition doesn't match") 59 | }) 60 | 61 | it('checks AppProxyPinned unstructured storage constants', async () => { 62 | // Set up AppStubPinnedStorage 63 | const fakeApp = await getContract('AppStub').new() 64 | const kernelMock = await getContract('KernelPinnedStorageMock').new(fakeApp.address) 65 | const pinnedProxy = await getContract('AppProxyPinnedStorageMock').new(kernelMock.address) 66 | 67 | assert.equal(await pinnedProxy.getPinnedCodePosition(), await keccakConstants.pinnedCodePosition(), "pinnedCodePosition doesn't match") 68 | }) 69 | 70 | it('checks DepositableStorage unstructured storage constants', async () => { 71 | const depositableMock = await getContract('DepositableStorageMock').new() 72 | assert.equal(await depositableMock.getDepositablePosition(), await keccakConstants.depositablePosition(), "depositablePosition doesn't match") 73 | }) 74 | 75 | it('checks Initializable unstructured storage constants', async () => { 76 | const initializableMock = await getContract('InitializableStorageMock').new() 77 | assert.equal(await initializableMock.getInitializationBlockPosition(), await keccakConstants.initializationBlockPosition(), "initializationBlockPosition doesn't match") 78 | }) 79 | 80 | it('checks ReentrancyGuard unstructured storage constants', async () => { 81 | const reentrancyGuardMock = await getContract('ReentrancyGuardMock').new() 82 | // Note that this is a bit of a roundabout test for this unstructured storage slot. Since the 83 | // position is declared as private in the base ReentrancyGuard contract, we redefine in the 84 | // mock. 85 | // This test therefore also relies on the ReentrancyGuard's own tests to make sure we've 86 | // redefined the storage position correctly in the mock. 87 | assert.equal(await reentrancyGuardMock.getReentrancyMutexPosition(), await keccakConstants.reentrancyGuardPosition(), "reentrancyGuardPosition doesn't match") 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /test/contracts/apps/app_acl.js: -------------------------------------------------------------------------------- 1 | const { hash } = require('eth-ens-namehash') 2 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | const { bn, onlyIf } = require('@aragon/contract-helpers-test') 4 | 5 | const ACL = artifacts.require('ACL') 6 | const Kernel = artifacts.require('Kernel') 7 | const KernelProxy = artifacts.require('KernelProxy') 8 | const AppProxyUpgradeable = artifacts.require('AppProxyUpgradeable') 9 | const AppProxyPinned = artifacts.require('AppProxyPinned') 10 | 11 | // Mocks 12 | const AppStub = artifacts.require('AppStub') 13 | const UnsafeAppStub = artifacts.require('UnsafeAppStub') 14 | 15 | const EMPTY_BYTES = '0x' 16 | const APP_ID = hash('stub.aragonpm.test') 17 | 18 | contract('App ACL', accounts => { 19 | let aclBase, kernelBase, acl, kernel 20 | let APP_BASES_NAMESPACE, APP_ROLE 21 | 22 | const permissionsRoot = accounts[0] 23 | const unauthorized = accounts[1] 24 | 25 | // Initial setup 26 | before(async () => { 27 | kernelBase = await Kernel.new(true) // petrify immediately 28 | aclBase = await ACL.new() 29 | 30 | // Setup constants 31 | APP_BASES_NAMESPACE = await kernelBase.APP_BASES_NAMESPACE() 32 | 33 | const app = await AppStub.new() 34 | APP_ROLE = await app.ROLE() 35 | }) 36 | 37 | beforeEach(async () => { 38 | kernel = await Kernel.at((await KernelProxy.new(kernelBase.address)).address) 39 | await kernel.initialize(aclBase.address, permissionsRoot) 40 | acl = await ACL.at(await kernel.acl()) 41 | 42 | // Set up app management permissions 43 | const APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE() 44 | await acl.createPermission(permissionsRoot, kernel.address, APP_MANAGER_ROLE, permissionsRoot) 45 | }) 46 | 47 | // Test the app itself and when it's behind the proxies to make sure their behaviours are the same 48 | const appProxyTypes = ['AppProxyUpgradeable', 'AppProxyPinned'] 49 | for (const appType of ['App', ...appProxyTypes]) { 50 | context(`> ${appType}`, () => { 51 | let appBase, app 52 | 53 | const onlyAppProxyUpgradeable = onlyIf(() => appType === 'AppProxyUpgradeable') 54 | 55 | before(async () => { 56 | if (appProxyTypes.includes(appType)) { 57 | // We can reuse the same app base for the proxies 58 | appBase = await AppStub.new() 59 | } 60 | }) 61 | 62 | beforeEach(async () => { 63 | if (appType === 'App') { 64 | // Use the unsafe version to use directly without a proxy 65 | app = await UnsafeAppStub.new(kernel.address) 66 | } else { 67 | await kernel.setApp(APP_BASES_NAMESPACE, APP_ID, appBase.address) 68 | let appProxy 69 | if (appType === 'AppProxyUpgradeable') { 70 | appProxy = await AppProxyUpgradeable.new(kernel.address, APP_ID, EMPTY_BYTES) 71 | } else if (appType === 'AppProxyPinned') { 72 | appProxy = await AppProxyPinned.new(kernel.address, APP_ID, EMPTY_BYTES) 73 | } 74 | app = await AppStub.at(appProxy.address) 75 | } 76 | 77 | await app.initialize() 78 | 79 | // assign app permissions 80 | await acl.createPermission(permissionsRoot, app.address, APP_ROLE, permissionsRoot) 81 | }) 82 | 83 | it('should return values correctly', async () => { 84 | assert.equal(await app.stringTest(), 'hola', 'string test') 85 | }) 86 | 87 | it('protected call works from authed entity', async () => { 88 | await app.setValue(10) 89 | assert.equal(await app.getValue(), 10, 'should have returned correct value') 90 | }) 91 | 92 | it('parametrized call works from authed entity if no params set', async () => { 93 | await app.setValueParam(11) 94 | assert.equal(await app.getValue(), 11, 'should have returned correct value') 95 | }) 96 | 97 | it('fails when called by unauthorized entity', async () => { 98 | await assertRevert(app.setValue(10, { from: unauthorized })) 99 | }) 100 | 101 | onlyAppProxyUpgradeable(() => 102 | it('fails if using app proxy without reference in kernel', async () => { 103 | const unknownId = hash('unknown.aragonpm.test') 104 | const appProxy = await AppProxyUpgradeable.new(kernel.address, unknownId, EMPTY_BYTES) 105 | const app = await AppStub.at(appProxy.address) 106 | 107 | await assertRevert(app.setValue(10)) 108 | }) 109 | ) 110 | 111 | context('> Parametrized calls', () => { 112 | const paramsGrantee = accounts[2] 113 | const paramValue = 5 114 | const succeedValue = paramValue + 1 115 | const failValue = paramValue - 1 116 | 117 | beforeEach(async () => { 118 | const argId = '0x00' // arg 0 119 | const op = '03' // greater than 120 | const value = `00000000000000000000000000000000000000000000000000000000000${paramValue}` // 5 121 | const param = bn(`${argId}${op}${value}`) 122 | 123 | await acl.grantPermissionP(paramsGrantee, app.address, APP_ROLE, [param], { from: permissionsRoot }) 124 | }) 125 | 126 | it('parametrized call succeeds if param eval succeeds', async () => { 127 | await app.setValueParam(succeedValue, { from: paramsGrantee }) 128 | }) 129 | 130 | it('parametrized call works from entity with no params set', async () => { 131 | // Fail value should still work for the entity who didn't have restrictions placed 132 | await app.setValueParam(failValue) 133 | assert.equal(await app.getValue(), failValue, 'should have returned correct value') 134 | }) 135 | 136 | it('parametrized app call fails if param eval fails', async () => { 137 | await assertRevert(app.setValueParam(failValue, { from: paramsGrantee })) 138 | }) 139 | }) 140 | }) 141 | } 142 | }) 143 | --------------------------------------------------------------------------------