diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index e426a58d22..5710665c54 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -79,10 +79,12 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain { external stoppable self returns (bool) { - // Prevent transactions to network contracts + // Prevent transactions to network contracts or network-managed extensions installed in this colony require(_to != address(this), "colony-cannot-target-self"); require(_to != colonyNetworkAddress, "colony-cannot-target-network"); require(_to != tokenLockingAddress, "colony-cannot-target-token-locking"); + require(isContract(_to), "colony-must-target-contract"); + require(!isOwnExtension(_to), "colony-cannot-target-own-extensions"); // Prevent transactions to transfer held tokens bytes4 sig; @@ -93,16 +95,6 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain { else if (sig == TRANSFER_SIG) { transferTransactionPreparation(_to, _action); } else if (sig == BURN_GUY_SIG || sig == TRANSFER_FROM_SIG) { burnGuyOrTransferFromTransactionPreparation(_action); } - // Prevent transactions to network-managed extensions installed in this colony - require(isContract(_to), "colony-to-must-be-contract"); - // slither-disable-next-line unused-return - try ColonyExtension(_to).identifier() returns (bytes32 extensionId) { - require( - IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) != _to, - "colony-cannot-target-extensions" - ); - } catch {} - bool res = executeCall(_to, 0, _action); if (sig == APPROVE_SIG) { approveTransactionCleanup(_to, _action); } @@ -378,22 +370,28 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain { IColonyNetwork(colonyNetworkAddress).installExtension(_extensionId, _version); } - function upgradeExtension(bytes32 _extensionId, uint256 _newVersion) + function upgradeExtension(address _extension, uint256 _newVersion) + public stoppable auth + { + IColonyNetwork(colonyNetworkAddress).upgradeExtension(_extension, _newVersion); + } + + function deprecateExtension(address _extension, bool _deprecated) public stoppable auth { - IColonyNetwork(colonyNetworkAddress).upgradeExtension(_extensionId, _newVersion); + IColonyNetwork(colonyNetworkAddress).deprecateExtension(_extension, _deprecated); } - function deprecateExtension(bytes32 _extensionId, bool _deprecated) + function uninstallExtension(address _extension) public stoppable auth { - IColonyNetwork(colonyNetworkAddress).deprecateExtension(_extensionId, _deprecated); + IColonyNetwork(colonyNetworkAddress).uninstallExtension(_extension); } - function uninstallExtension(bytes32 _extensionId) + function migrateToMultiExtension(bytes32 _extensionId) public stoppable auth { - IColonyNetwork(colonyNetworkAddress).uninstallExtension(_extensionId); + IColonyNetwork(colonyNetworkAddress).migrateToMultiExtension(_extensionId); } function addDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _parentDomainId) public @@ -501,6 +499,15 @@ contract Colony is ColonyStorage, PatriciaTreeProofs, MultiChain { sig = bytes4(keccak256("setDefaultGlobalClaimDelay(uint256)")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + sig = bytes4(keccak256("upgradeExtension(address,uint256)")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + + sig = bytes4(keccak256("deprecateExtension(address,bool)")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + + sig = bytes4(keccak256("uninstallExtension(address)")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + sig = bytes4(keccak256("setExpenditureMetadata(uint256,uint256,uint256,string)")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true); } diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index b89f53aeaa..0b040f60fb 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -117,6 +117,10 @@ contract ColonyAuthority is CommonAuthority { // Added in colony v8 (ebony-lwss) addRoleCapability(ROOT_ROLE, "makeArbitraryTransactions(address[],bytes[],bool)"); addRoleCapability(ROOT_ROLE, "setDefaultGlobalClaimDelay(uint256)"); + addRoleCapability(ROOT_ROLE, "upgradeExtension(address,uint256)"); + addRoleCapability(ROOT_ROLE, "deprecateExtension(address,bool)"); + addRoleCapability(ROOT_ROLE, "uninstallExtension(address)"); + addRoleCapability(ROOT_ROLE, "migrateToMultiExtension(bytes32)"); addRoleCapability(ARBITRATION_ROLE, "setExpenditureMetadata(uint256,uint256,uint256,string)"); } diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index 805d9e1e4e..53fbd3c5df 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -239,19 +239,7 @@ contract ColonyStorage is CommonStorage, ColonyDataTypes, ColonyNetworkDataTypes } modifier onlyExtension() { - // Ensure msg.sender is a contract - require(isContract(msg.sender), "colony-sender-must-be-contract"); - - // Ensure msg.sender is an extension - // slither-disable-next-line unused-return - try ColonyExtension(msg.sender).identifier() returns (bytes32 extensionId) { - require( - IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) == msg.sender, - "colony-must-be-extension" - ); - } catch { - require(false, "colony-must-be-extension"); - } + require(isOwnExtension(msg.sender), "colony-must-be-extension"); _; } @@ -300,6 +288,23 @@ contract ColonyStorage is CommonStorage, ColonyDataTypes, ColonyNetworkDataTypes return size > 0; } + // slither-disable-next-line unused-return + function isOwnExtension(address addr) internal returns (bool) { + // Ensure addr is a contract first, otherwise `try` block will revert + if (!isContract(addr)) { return false; } + + // Ensure addr is an extension installed in the colony, must check old & new formats + try ColonyExtension(addr).identifier() returns (bytes32 extensionId) { + return ( + IColonyNetwork(colonyNetworkAddress).getExtensionColony(addr) == address(this) || + IColonyNetwork(colonyNetworkAddress).getExtensionInstallation(extensionId, address(this)) == addr + ); + } catch { + return false; + } + + } + function domainExists(uint256 domainId) internal view returns (bool) { return domainId > 0 && domainId <= domainCount; } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index c56760608c..d16c742b0f 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -266,20 +266,24 @@ interface IColony is ColonyDataTypes, IRecovery { function installExtension(bytes32 extensionId, uint256 version) external; /// @notice Upgrade an extension in a colony. Secured function to authorised members. - /// @param extensionId keccak256 hash of the extension name, used as an indentifier + /// @param extension The address of the extension installation /// @param newVersion The version to upgrade to (must be one larger than the current version) - function upgradeExtension(bytes32 extensionId, uint256 newVersion) external; + function upgradeExtension(address extension, uint256 newVersion) external; /// @notice Set the deprecation of an extension in a colony. Secured function to authorised members. - /// @param extensionId keccak256 hash of the extension name, used as an indentifier + /// @param extension The address of the extension installation /// @param deprecated Whether to deprecate the extension or not - function deprecateExtension(bytes32 extensionId, bool deprecated) external; + function deprecateExtension(address extension, bool deprecated) external; /// @notice Uninstall an extension from a colony. Secured function to authorised members. /// @dev This is a permanent action -- re-installing the extension will deploy a new contract /// @dev It is recommended to deprecate an extension before uninstalling to allow active objects to be resolved + /// @param extension The address of the extension installation + function uninstallExtension(address extension) external; + + /// @notice Migrate extension bookkeeping to multiExtension. Secured function to authorised members. /// @param extensionId keccak256 hash of the extension name, used as an indentifier - function uninstallExtension(bytes32 extensionId) external; + function migrateToMultiExtension(bytes32 extensionId) external; /// @notice Add a colony domain, and its respective local skill under skill with id `_parentSkillId`. /// New funding pot is created and associated with the domain here. diff --git a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol index 2ef049b925..ff57758b88 100755 --- a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol +++ b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol @@ -114,29 +114,62 @@ interface ColonyNetworkDataTypes { /// @param version The version of the extension event ExtensionAddedToNetwork(bytes32 indexed extensionId, uint256 version); - /// @notice Event logged when an extension is installed in a colony + /// @notice Event logged when an extension is installed in a colony (for v7 and below) /// @param extensionId The identifier for the extension /// @param colony The address of the colony /// @param version The version of the extension event ExtensionInstalled(bytes32 indexed extensionId, address indexed colony, uint256 version); + /// @notice Event logged when an extension is installed in a colony (for v8 and above) + /// @param extensionId The identifier for the extension + /// @param extension Address of the extension installation + /// @param colony The address of the colony + /// @param version The version of the extension + event ExtensionInstalled(bytes32 indexed extensionId, address indexed extension, address indexed colony, uint256 version); + + /// @dev DEPRECATED /// @notice Event logged when an extension is upgraded in a colony /// @param extensionId The identifier for the extension /// @param colony The address of the colony /// @param version The new version of the extension event ExtensionUpgraded(bytes32 indexed extensionId, address indexed colony, uint256 version); + /// @notice Event logged when an extension is upgraded in a colony + /// @param extension Address of the extension installation + /// @param colony The address of the colony + /// @param version The new version of the extension + event ExtensionUpgraded(address indexed extension, address indexed colony, uint256 version); + + /// @dev DEPRECATED /// @notice Event logged when an extension is (un)deprecated in a colony /// @param extensionId The identifier for the extension /// @param colony The address of the colony /// @param deprecated Whether the extension is deprecated or not event ExtensionDeprecated(bytes32 indexed extensionId, address indexed colony, bool deprecated); + /// @notice Event logged when an extension is (un)deprecated in a colony + /// @param extension Address of the extension installation + /// @param colony The address of the colony + /// @param deprecated Whether the extension is deprecated or not + event ExtensionDeprecated(address indexed extension, address indexed colony, bool deprecated); + + /// @dev DEPRECATED /// @notice Event logged when an extension is uninstalled from a colony /// @param extensionId The identifier for the extension /// @param colony The address of the colony event ExtensionUninstalled(bytes32 indexed extensionId, address indexed colony); + /// @notice Event logged when an extension is uninstalled from a colony + /// @param extension Address of the extension installation + /// @param colony The address of the colony + event ExtensionUninstalled(address indexed extension, address indexed colony); + + /// @notice Event logged when an extension is migrated from old to new storage schemes + /// @param extensionId The identifier for the extension + /// @param colony The address of the colony + /// @param extension Address of the extension installation + event ExtensionMigrated(bytes32 indexed extensionId, address indexed colony, address extension); + struct Skill { // total number of parent skills uint128 nParents; diff --git a/contracts/colonyNetwork/ColonyNetworkExtensions.sol b/contracts/colonyNetwork/ColonyNetworkExtensions.sol index 6c27b8b75b..b3ba26fa05 100644 --- a/contracts/colonyNetwork/ColonyNetworkExtensions.sol +++ b/contracts/colonyNetwork/ColonyNetworkExtensions.sol @@ -50,19 +50,32 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage { public stoppable calledByColony + returns (address) { require(resolvers[_extensionId][_version] != address(0x0), "colony-network-extension-bad-version"); - require(installations[_extensionId][msg.sender] == address(0x0), "colony-network-extension-already-installed"); EtherRouter extension = new EtherRouter(); - installations[_extensionId][msg.sender] = address(extension); + + // Install in old installations mapping if version 7 or below + if (IColony(msg.sender).version() <= 7) { + require(installations[_extensionId][msg.sender] == address(0x0), "colony-network-extension-already-installed"); + installations[_extensionId][msg.sender] = address(extension); + + emit ExtensionInstalled(_extensionId, address(extension), _version); + } else { + multiInstallations[address(extension)] = msg.sender; + + emit ExtensionInstalled(_extensionId, address(extension), msg.sender, _version); + } extension.setResolver(resolvers[_extensionId][_version]); ColonyExtension(address(extension)).install(msg.sender); - emit ExtensionInstalled(_extensionId, msg.sender, _version); + + return address(extension); } + // Deprecated function upgradeExtension(bytes32 _extensionId, uint256 _newVersion) public stoppable @@ -81,6 +94,27 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage { emit ExtensionUpgraded(_extensionId, msg.sender, _newVersion); } + function upgradeExtension(address _extension, uint256 _newVersion) + public + stoppable + calledByColony + { + require(multiInstallations[_extension] == msg.sender, "colony-network-extension-not-installed"); + + bytes32 extensionId = ColonyExtension(_extension).identifier(); + + require(_newVersion == ColonyExtension(_extension).version() + 1, "colony-network-extension-bad-increment"); + require(resolvers[extensionId][_newVersion] != address(0x0), "colony-network-extension-bad-version"); + + EtherRouter(payable(_extension)).setResolver(resolvers[extensionId][_newVersion]); + ColonyExtension(_extension).finishUpgrade(); + + assert(ColonyExtension(_extension).version() == _newVersion); + + emit ExtensionUpgraded(_extension, msg.sender, _newVersion); + } + + // Deprecated function deprecateExtension(bytes32 _extensionId, bool _deprecated) public stoppable @@ -91,6 +125,17 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage { emit ExtensionDeprecated(_extensionId, msg.sender, _deprecated); } + function deprecateExtension(address _extension, bool _deprecated) + public + stoppable + calledByColony + { + ColonyExtension(_extension).deprecate(_deprecated); + + emit ExtensionDeprecated(_extension, msg.sender, _deprecated); + } + + // Deprecated function uninstallExtension(bytes32 _extensionId) public stoppable @@ -99,12 +144,41 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage { require(installations[_extensionId][msg.sender] != address(0x0), "colony-network-extension-not-installed"); ColonyExtension extension = ColonyExtension(installations[_extensionId][msg.sender]); - installations[_extensionId][msg.sender] = address(0x0); + delete installations[_extensionId][msg.sender]; extension.uninstall(); emit ExtensionUninstalled(_extensionId, msg.sender); } + function uninstallExtension(address _extension) + public + stoppable + calledByColony + { + require(multiInstallations[_extension] == msg.sender, "colony-network-extension-not-installed"); + + delete multiInstallations[_extension]; + ColonyExtension(_extension).uninstall(); + + emit ExtensionUninstalled(_extension, msg.sender); + } + + function migrateToMultiExtension(bytes32 _extensionId) + public + stoppable + calledByColony + { + address extension = installations[_extensionId][msg.sender]; + + require(extension != address(0x0), "colony-network-extension-not-installed"); + + multiInstallations[extension] = payable(msg.sender); + + delete installations[_extensionId][msg.sender]; + + emit ExtensionMigrated(_extensionId, msg.sender, extension); + } + // Public view functions function getExtensionResolver(bytes32 _extensionId, uint256 _version) @@ -115,6 +189,14 @@ contract ColonyNetworkExtensions is ColonyNetworkStorage { return resolvers[_extensionId][_version]; } + function getExtensionColony(address _extension) + public + view + returns (address) + { + return multiInstallations[_extension]; + } + function getExtensionInstallation(bytes32 _extensionId, address _colony) public view diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index dc846174e3..b8f726afdb 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -93,13 +93,16 @@ contract ColonyNetworkStorage is CommonStorage, ColonyNetworkDataTypes, DSMath { uint256 DEPRECATED_lastMetaColonyStipendIssued; // Storage slot 37 // [_extensionId][version] => resolver - mapping(bytes32 => mapping(uint256 => address)) resolvers; // Storage slot 38 + mapping (bytes32 => mapping(uint256 => address)) resolvers; // Storage slot 38 // [_extensionId][colony] => address - mapping(bytes32 => mapping(address => address payable)) installations; // Storage slot 39 + mapping (bytes32 => mapping(address => address payable)) installations; // Storage slot 39 // Used for whitelisting payout tokens mapping (address => bool) payoutWhitelist; // Storage slot 40 + // [_extension] => colony + mapping (address => address payable) multiInstallations; // Storage slot 41 + modifier calledByColony() { require(_isColony[msg.sender], "colony-caller-must-be-colony"); _; diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index 3a5a696b9f..4fb12210a7 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -318,20 +318,41 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery { /// @param version Version of the extension to install function installExtension(bytes32 extensionId, uint256 version) external; + /// @dev DEPRECATED /// @notice Upgrade an extension in a colony. Can only be called by a Colony. /// @param extensionId keccak256 hash of the extension name, used as an indentifier /// @param newVersion Version of the extension to upgrade to (must be one greater than current) function upgradeExtension(bytes32 extensionId, uint256 newVersion) external; + /// @notice Upgrade an extension in a colony. Can only be called by a Colony. + /// @param extension Address of the extension installation + /// @param newVersion Version of the extension to upgrade to (must be one greater than current) + function upgradeExtension(address extension, uint256 newVersion) external; + + /// @dev DEPRECATED /// @notice Set the deprecation of an extension in a colony. Can only be called by a Colony. /// @param extensionId keccak256 hash of the extension name, used as an indentifier /// @param deprecated Whether to deprecate the extension or not function deprecateExtension(bytes32 extensionId, bool deprecated) external; + /// @notice Set the deprecation of an extension in a colony. Can only be called by a Colony. + /// @param extension Address of the extension installation + /// @param deprecated Whether to deprecate the extension or not + function deprecateExtension(address extension, bool deprecated) external; + + /// @dev DEPRECATED /// @notice Uninstall an extension in a colony. Can only be called by a Colony. /// @param extensionId keccak256 hash of the extension name, used as an indentifier function uninstallExtension(bytes32 extensionId) external; + /// @notice Uninstall an extension in a colony. Can only be called by a Colony. + /// @param extension Address of the extension installation + function uninstallExtension(address extension) external; + + /// @notice Migrate extension bookkeeping to multiExtension. Can only be called by a Colony. + /// @param extensionId keccak256 hash of the extension name, used as an indentifier + function migrateToMultiExtension(bytes32 extensionId) external; + /// @notice Get an extension's resolver. /// @param extensionId keccak256 hash of the extension name, used as an indentifier /// @param version Version of the extension @@ -344,6 +365,11 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery { /// @return installation The address of the installed extension function getExtensionInstallation(bytes32 extensionId, address colony) external view returns (address installation); + /// @notice Get an extension's installed colony. + /// @param extension Address of the extension installation + /// @return colony Address of the colony the extension is installed in + function getExtensionColony(address extension) external view returns (address colony); + /// @notice Return 1 / the fee to pay to the network. e.g. if the fee is 1% (or 0.01), return 100. /// @return _feeInverse The inverse of the network fee function getFeeInverse() external view returns (uint256 _feeInverse); diff --git a/contracts/testHelpers/PreviousVersion.sol b/contracts/testHelpers/PreviousVersion.sol index bcb10479ab..7fec757173 100644 --- a/contracts/testHelpers/PreviousVersion.sol +++ b/contracts/testHelpers/PreviousVersion.sol @@ -1,5 +1,7 @@ pragma solidity 0.7.3; +import "./../colonyNetwork/IColonyNetwork.sol"; + contract Version3 { function version() pure external returns (uint256) { return 3; @@ -10,4 +12,32 @@ contract Version4 { function version() pure external returns (uint256) { return 4; } -} \ No newline at end of file +} + +contract Version7 { + function version() public pure returns (uint256) { + return 7; + } + + address colonyNetworkAddress; + + constructor(address _colonyNetworkAddress) public { + colonyNetworkAddress = _colonyNetworkAddress; + } + + function installExtension(bytes32 _extensionId, uint256 _version) public { + IColonyNetwork(colonyNetworkAddress).installExtension(_extensionId, _version); + } + + function upgradeExtension(bytes32 _extensionId, uint256 _newVersion) public { + IColonyNetwork(colonyNetworkAddress).upgradeExtension(_extensionId, _newVersion); + } + + function deprecateExtension(bytes32 _extensionId, bool _deprecated) public { + IColonyNetwork(colonyNetworkAddress).deprecateExtension(_extensionId, _deprecated); + } + + function uninstallExtension(bytes32 _extensionId) public { + IColonyNetwork(colonyNetworkAddress).uninstallExtension(_extensionId); + } +} diff --git a/docs/_Interface_IColony.md b/docs/_Interface_IColony.md index 0f6d187444..ab8ccee0ca 100644 --- a/docs/_Interface_IColony.md +++ b/docs/_Interface_IColony.md @@ -257,7 +257,7 @@ Set the deprecation of an extension in a colony. Secured function to authorised |Name|Type|Description| |---|---|---| -|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier +|extension|address|The address of the extension installation |deprecated|bool|Whether to deprecate the extension or not @@ -1155,6 +1155,18 @@ Make a new task in the colony. Secured function to authorised members. |_dueDate|uint256|The due date of the task, can set to `0` for no-op +### `migrateToMultiExtension` + +Migrate extension bookkeeping to multiExtension. Secured function to authorised members. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier + + ### `mintTokens` Mint `_wad` amount of colony tokens. Secured function to authorised members. @@ -1894,7 +1906,7 @@ Uninstall an extension from a colony. Secured function to authorised members. |Name|Type|Description| |---|---|---| -|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier +|extension|address|The address of the extension installation ### `unlockToken` @@ -1964,7 +1976,7 @@ Upgrade an extension in a colony. Secured function to authorised members. |Name|Type|Description| |---|---|---| -|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier +|extension|address|The address of the extension installation |newVersion|uint256|The version to upgrade to (must be one larger than the current version) diff --git a/docs/_Interface_IColonyNetwork.md b/docs/_Interface_IColonyNetwork.md index e0a507aa88..a15923d2c4 100644 --- a/docs/_Interface_IColonyNetwork.md +++ b/docs/_Interface_IColonyNetwork.md @@ -226,6 +226,19 @@ Create the Meta Colony, same as a normal colony plus the root skill. Set the deprecation of an extension in a colony. Can only be called by a Colony. +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extension|address|Address of the extension installation +|deprecated|bool|Whether to deprecate the extension or not + + +### `deprecateExtension` + +Set the deprecation of an extension in a colony. Can only be called by a Colony. + + **Parameters** |Name|Type|Description| @@ -334,6 +347,23 @@ Returns the address of the ENSRegistrar for the Network. |---|---|---| |address|address|The address the ENSRegistrar resolves to +### `getExtensionColony` + +Get an extension's installed colony. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extension|address|Address of the extension installation + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|colony|address|Address of the colony the extension is installed in + ### `getExtensionInstallation` Get an extension's installation. @@ -698,6 +728,18 @@ Reverse lookup a username from an address. |---|---|---| |domain|string|A string containing the colony-based ENS name corresponding to addr +### `migrateToMultiExtension` + +Migrate extension bookkeeping to multiExtension. Can only be called by a Colony. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extensionId|bytes32|keccak256 hash of the extension name, used as an indentifier + + ### `punishStakers` Function called to punish people who staked against a new reputation root hash that turned out to be incorrect. @@ -930,6 +972,18 @@ Query if a contract implements an interface Uninstall an extension in a colony. Can only be called by a Colony. +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extension|address|Address of the extension installation + + +### `uninstallExtension` + +Uninstall an extension in a colony. Can only be called by a Colony. + + **Parameters** |Name|Type|Description| @@ -978,6 +1032,19 @@ Update a user's orbitdb address. Can only be called by a user with a registered Upgrade an extension in a colony. Can only be called by a Colony. +**Parameters** + +|Name|Type|Description| +|---|---|---| +|extension|address|Address of the extension installation +|newVersion|uint256|Version of the extension to upgrade to (must be one greater than current) + + +### `upgradeExtension` + +Upgrade an extension in a colony. Can only be called by a Colony. + + **Parameters** |Name|Type|Description| diff --git a/helpers/test-helper.js b/helpers/test-helper.js index 69b4c86dae..44f1ac31a0 100644 --- a/helpers/test-helper.js +++ b/helpers/test-helper.js @@ -1004,3 +1004,7 @@ export function bn2bytes32(x, size = 64) { export function rolesToBytes32(roles) { return `0x${new BN(roles.map((role) => new BN(1).shln(role)).reduce((a, b) => a.or(b), new BN(0))).toString(16, 64)}`; } + +export function getExtensionAddressFromTx(installExtensionTx) { + return `0x${installExtensionTx.receipt.rawLogs[1].topics[2].slice(26)}`; +} diff --git a/test-smoke/colony-storage-consistent.js b/test-smoke/colony-storage-consistent.js index 78eb1800f9..4d6c0136fb 100644 --- a/test-smoke/colony-storage-consistent.js +++ b/test-smoke/colony-storage-consistent.js @@ -154,11 +154,11 @@ contract("Contract Storage", (accounts) => { console.log("miningCycleStateHash:", miningCycleAccount.stateRoot.toString("hex")); console.log("tokenLockingStateHash:", tokenLockingAccount.stateRoot.toString("hex")); - expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("55779b88bac4206c6ed791b93fa3a386c2e71b52d896d262c6b69c476a6968fe"); - expect(colonyAccount.stateRoot.toString("hex")).to.equal("abe2e24d6c366741cfb171ba03e452a705ef3ffce1715348fdfaccbdfd44fab2"); - expect(metaColonyAccount.stateRoot.toString("hex")).to.equal("78294685a492256887e3159e26071ba06d62883c72ccb26fd323a883eefc30fd"); - expect(miningCycleAccount.stateRoot.toString("hex")).to.equal("e105190bcd647989da1579ac209ae54ed63e08224fbb2469bad9f596773fe558"); - expect(tokenLockingAccount.stateRoot.toString("hex")).to.equal("8bab6ab2024de44a08765ee14ec3d6bdc0fa5ae2a5ee221c9f928345a3710658"); + expect(colonyNetworkAccount.stateRoot.toString("hex")).to.equal("20aafab74e93166b4453d850146ef4821336bd0ba3126700553bcf72dd919461"); + expect(colonyAccount.stateRoot.toString("hex")).to.equal("819d48ad7078741c91b128b1797f746568531a49f062d3bb251c02194b3974ac"); + expect(metaColonyAccount.stateRoot.toString("hex")).to.equal("9667e38b9b92fd7a405f8ed66ceb154d835ada404751b24a3186f2cdc665b9eb"); + expect(miningCycleAccount.stateRoot.toString("hex")).to.equal("6b36b3736e5360f9cd3af153a820728423e1e4589db8a65d2ac267447ee5b7c5"); + expect(tokenLockingAccount.stateRoot.toString("hex")).to.equal("01074ed59377000ae7e9222e7c5c8d25c6df11f851e15db9d8d56bd2915b182d"); }); }); }); diff --git a/test/contracts-network/colony-arbitrary-transactions.js b/test/contracts-network/colony-arbitrary-transactions.js index a29e7ee53e..ffffb164ee 100644 --- a/test/contracts-network/colony-arbitrary-transactions.js +++ b/test/contracts-network/colony-arbitrary-transactions.js @@ -6,7 +6,7 @@ import { ethers } from "ethers"; import { soliditySha3 } from "web3-utils"; import { UINT256_MAX, WAD } from "../../helpers/constants"; -import { checkErrorRevert, encodeTxData } from "../../helpers/test-helper"; +import { checkErrorRevert, encodeTxData, getExtensionAddressFromTx } from "../../helpers/test-helper"; import { setupRandomColony, fundColonyWithTokens } from "../../helpers/test-data-generator"; const { expect } = chai; @@ -103,7 +103,7 @@ contract("Colony Arbitrary Transactions", (accounts) => { }); it("should not be able to make arbitrary transactions to a user address", async () => { - await checkErrorRevert(colony.makeArbitraryTransaction(accounts[0], "0x0"), "colony-to-must-be-contract"); + await checkErrorRevert(colony.makeArbitraryTransaction(accounts[0], "0x0"), "colony-must-target-contract"); }); it("should not be able to make arbitrary transactions to network or token locking", async () => { @@ -206,16 +206,16 @@ contract("Colony Arbitrary Transactions", (accounts) => { it("should not be able to make arbitrary transactions to the colony's own extensions", async () => { const COIN_MACHINE = soliditySha3("CoinMachine"); - await colony.installExtension(COIN_MACHINE, 2); + const tx = await colony.installExtension(COIN_MACHINE, 2); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); + const coinMachineAddress = getExtensionAddressFromTx(tx); const coinMachine = await CoinMachine.at(coinMachineAddress); await coinMachine.initialise(token.address, ethers.constants.AddressZero, 60 * 60, 10, WAD, WAD, WAD, WAD, ADDRESS_ZERO); await token.mint(coinMachine.address, WAD); const action = await encodeTxData(coinMachine, "buyTokens", [WAD]); - await checkErrorRevert(colony.makeArbitraryTransaction(coinMachine.address, action), "colony-cannot-target-extensions"); + await checkErrorRevert(colony.makeArbitraryTransaction(coinMachine.address, action), "colony-cannot-target-own-extensions"); // But other colonies can const { colony: otherColony } = await setupRandomColony(colonyNetwork); diff --git a/test/contracts-network/colony-network-extensions.js b/test/contracts-network/colony-network-extensions.js index 9e89c309ef..bc14246989 100644 --- a/test/contracts-network/colony-network-extensions.js +++ b/test/contracts-network/colony-network-extensions.js @@ -6,7 +6,7 @@ import { BN } from "bn.js"; import { ethers } from "ethers"; import { soliditySha3 } from "web3-utils"; -import { checkErrorRevert, web3GetBalance, encodeTxData } from "../../helpers/test-helper"; +import { checkErrorRevert, web3GetBalance, encodeTxData, expectEvent, getExtensionAddressFromTx } from "../../helpers/test-helper"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; import { setupColonyNetwork, setupMetaColonyWithLockedCLNYToken, setupRandomColony } from "../../helpers/test-data-generator"; import { UINT256_MAX } from "../../helpers/constants"; @@ -25,9 +25,12 @@ const TestExtension3 = artifacts.require("TestExtension3"); const TestVotingToken = artifacts.require("TestVotingToken"); const Resolver = artifacts.require("Resolver"); const RequireExecuteCall = artifacts.require("RequireExecuteCall"); +const ContractEditing = artifacts.require("ContractEditing"); +const Version7 = artifacts.require("Version7"); contract("Colony Network Extensions", (accounts) => { let colonyNetwork; + let editableColonyNetwork; let metaColony; let colony; let token; @@ -71,6 +74,13 @@ contract("Colony Network Extensions", (accounts) => { colonyNetwork = await setupColonyNetwork(); ({ metaColony } = await setupMetaColonyWithLockedCLNYToken(colonyNetwork)); + const colonyNetworkAsER = await EtherRouter.at(colonyNetwork.address); + const colonyNetworkResolverAddress = await colonyNetworkAsER.resolver(); + const colonyNetworkResolver = await Resolver.at(colonyNetworkResolverAddress); + const contractEditing = await ContractEditing.new(); + await colonyNetworkResolver.register("setStorageSlot(uint256,bytes32)", contractEditing.address); + editableColonyNetwork = await ContractEditing.at(colonyNetwork.address); + ({ colony, token } = await setupRandomColony(colonyNetwork)); await colony.addDomain(1, UINT256_MAX, 1); // Domain 2 @@ -145,11 +155,11 @@ contract("Colony Network Extensions", (accounts) => { await metaColony.addExtensionToNetwork(TEST_EXTENSION, testExtension2Resolver.address); }); - it("allows a root user to install an extension with any version", async () => { - await colony.installExtension(TEST_EXTENSION, 2, { from: ROOT }); + it("allows a root user to install an extension", async () => { + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); - const extension = await TestExtension2.at(extensionAddress); + const extensionAddress = getExtensionAddressFromTx(tx); + const extension = await TestExtension1.at(extensionAddress); const owner = await extension.owner(); expect(owner).to.equal(colonyNetwork.address); @@ -157,21 +167,72 @@ contract("Colony Network Extensions", (accounts) => { const version = await extension.version(); const colonyAddress = await extension.getColony(); expect(identifier).to.equal(TEST_EXTENSION); - expect(version).to.eq.BN(2); + expect(version).to.eq.BN(1); expect(colonyAddress).to.equal(colony.address); // Only colonyNetwork can install the extension await checkErrorRevert(extension.install(colony.address), "ds-auth-unauthorized"); }); + it("allows a root user to install an extension with any version", async () => { + const tx = await colony.installExtension(TEST_EXTENSION, 2, { from: ROOT }); + + const extensionAddress = getExtensionAddressFromTx(tx); + const extension = await TestExtension2.at(extensionAddress); + + const identifier = await extension.identifier(); + const version = await extension.version(); + expect(identifier).to.equal(TEST_EXTENSION); + expect(version).to.eq.BN(2); + }); + it("does not allow an extension to be installed with a nonexistent resolver", async () => { await checkErrorRevert(colony.installExtension(TEST_EXTENSION, 0, { from: ROOT }), "colony-network-extension-bad-version"); }); - it("does not allow an extension to be installed twice", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + it("allows colonies to migrate to multiExtension bookkeeping", async () => { + const extension = await TestExtension1.new(); + await extension.install(colony.address); + + let colonyAddress; + + colonyAddress = await colonyNetwork.getExtensionColony(extension.address); + expect(colonyAddress).to.equal(ethers.constants.AddressZero); + + // Set up `installations` mapping in the old style + const slot = soliditySha3(ethers.utils.hexZeroPad(colony.address, 32), soliditySha3(TEST_EXTENSION, 39)); + const value = ethers.utils.hexZeroPad(extension.address, 32); + await editableColonyNetwork.setStorageSlot(slot, value); + + let extensionAddress; + extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); + expect(extensionAddress).to.not.equal(ethers.constants.AddressZero); + + const tx = await colony.migrateToMultiExtension(TEST_EXTENSION); + await expectEvent(tx, "ExtensionMigrated(bytes32 indexed,address indexed,address)", [TEST_EXTENSION, colony.address, extensionAddress]); + + colonyAddress = await colonyNetwork.getExtensionColony(extension.address); + expect(colonyAddress).to.equal(colony.address); + + extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); + expect(extensionAddress).to.equal(ethers.constants.AddressZero); + }); + + it("allows old colonies to install extensions correctly", async () => { + const version7Colony = await Version7.new(colonyNetwork.address); + + // Add version7Colony to _isColony mapping + const slot = soliditySha3(ethers.utils.hexZeroPad(version7Colony.address, 32), 19); + const value = ethers.utils.zeroPad(1, 32); + await editableColonyNetwork.setStorageSlot(slot, value); - await checkErrorRevert(colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }), "colony-network-extension-already-installed"); + await version7Colony.installExtension(TEST_EXTENSION, 1); + + const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, version7Colony.address); + expect(extensionAddress).to.not.equal(ethers.constants.AddressZero); + + // But not twice + await checkErrorRevert(version7Colony.installExtension(TEST_EXTENSION, 1), "colony-network-extension-already-installed"); }); }); @@ -183,16 +244,14 @@ contract("Colony Network Extensions", (accounts) => { }); it("allows root users to upgrade an extension", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); - - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); - expect(extensionAddress).to.not.equal(ethers.constants.AddressZero); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const extensionAddress = getExtensionAddressFromTx(tx); let extension = await ColonyExtension.at(extensionAddress); let version = await extension.version(); expect(version).to.eq.BN(1); - await colony.upgradeExtension(TEST_EXTENSION, 2, { from: ROOT }); + await colony.methods["upgradeExtension(address,uint256)"](extensionAddress, 2, { from: ROOT }); extension = await ColonyExtension.at(extensionAddress); version = await extension.version(); @@ -200,27 +259,58 @@ contract("Colony Network Extensions", (accounts) => { }); it("does not allow non-root users to upgrade an extension", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); - await checkErrorRevert(colony.upgradeExtension(TEST_EXTENSION, 2, { from: ARCHITECT }), "ds-auth-unauthorized"); - await checkErrorRevert(colony.upgradeExtension(TEST_EXTENSION, 2, { from: USER }), "ds-auth-unauthorized"); + const extensionAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["upgradeExtension(address,uint256)"](extensionAddress, 2, { from: ARCHITECT }), "ds-auth-unauthorized"); + await checkErrorRevert(colony.methods["upgradeExtension(address,uint256)"](extensionAddress, 2, { from: USER }), "ds-auth-unauthorized"); }); it("does not allow upgrading a extension which is not installed", async () => { - await checkErrorRevert(colony.upgradeExtension(TEST_EXTENSION, 2, { from: ROOT }), "colony-network-extension-not-installed"); + await checkErrorRevert( + colony.methods["upgradeExtension(address,uint256)"](ethers.constants.AddressZero, 2, { from: ROOT }), + "colony-network-extension-not-installed" + ); }); it("does not allow upgrading a extension to a version which does not exist", async () => { - await colony.installExtension(TEST_EXTENSION, 3, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 3, { from: ROOT }); // Can't upgrade from version 3 to nonexistent 4 - await checkErrorRevert(colony.upgradeExtension(TEST_EXTENSION, 4, { from: ROOT }), "colony-network-extension-bad-version"); + const extensionAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert( + colony.methods["upgradeExtension(address,uint256)"](extensionAddress, 4, { from: ROOT }), + "colony-network-extension-bad-version" + ); }); it("does not allow upgrading a extension out of order", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + + const extensionAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert( + colony.methods["upgradeExtension(address,uint256)"](extensionAddress, 3, { from: ROOT }), + "colony-network-extension-bad-increment" + ); + }); + + it("allows old colonies to upgrade extensions correctly", async () => { + const version7Colony = await Version7.new(colonyNetwork.address); + + // Add version7Colony to _isColony mapping + const slot = soliditySha3(ethers.utils.hexZeroPad(version7Colony.address, 32), 19); + const value = ethers.utils.zeroPad(1, 32); + await editableColonyNetwork.setStorageSlot(slot, value); - await checkErrorRevert(colony.upgradeExtension(TEST_EXTENSION, 3, { from: ROOT }), "colony-network-extension-bad-increment"); + await version7Colony.installExtension(TEST_EXTENSION, 1); + + const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, version7Colony.address); + + await version7Colony.upgradeExtension(TEST_EXTENSION, 2); + + const extension = await ColonyExtension.at(extensionAddress); + const version = await extension.version(); + expect(version).to.eq.BN(2); }); }); @@ -230,26 +320,45 @@ contract("Colony Network Extensions", (accounts) => { }); it("allows root users to deprecate and undeprecate an extension", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); + const extensionAddress = getExtensionAddressFromTx(tx); const extension = await TestExtension1.at(extensionAddress); await extension.foo(); - await colony.deprecateExtension(TEST_EXTENSION, true, { from: ROOT }); + await colony.methods["deprecateExtension(address,bool)"](extensionAddress, true, { from: ROOT }); await checkErrorRevert(extension.foo(), "colony-extension-deprecated"); - await colony.deprecateExtension(TEST_EXTENSION, false, { from: ROOT }); + await colony.methods["deprecateExtension(address,bool)"](extensionAddress, false, { from: ROOT }); await extension.foo(); }); it("does not allow non-root users to deprecate an extension", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + + const extensionAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["deprecateExtension(address,bool)"](extensionAddress, true, { from: ARCHITECT }), "ds-auth-unauthorized"); + }); + + it("allows old colonies to deprecate extensions correctly", async () => { + const version7Colony = await Version7.new(colonyNetwork.address); + + // Add version7Colony to _isColony mapping + const slot = soliditySha3(ethers.utils.hexZeroPad(version7Colony.address, 32), 19); + const value = ethers.utils.zeroPad(1, 32); + await editableColonyNetwork.setStorageSlot(slot, value); - await checkErrorRevert(colony.deprecateExtension(TEST_EXTENSION, true, { from: ARCHITECT }), "ds-auth-unauthorized"); + await version7Colony.installExtension(TEST_EXTENSION, 1); + + const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, version7Colony.address); + const extension = await TestExtension1.at(extensionAddress); + + await version7Colony.deprecateExtension(TEST_EXTENSION, true); + + await checkErrorRevert(extension.foo(), "colony-extension-deprecated"); }); }); @@ -259,29 +368,50 @@ contract("Colony Network Extensions", (accounts) => { }); it("allows root users to uninstall an extension and send ether to the beneficiary", async () => { - await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); + const tx = await colony.installExtension(TEST_EXTENSION, 1, { from: ROOT }); - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, colony.address); + const extensionAddress = getExtensionAddressFromTx(tx); const extension = await TestExtension1.at(extensionAddress); await extension.send(100); // Only colonyNetwork can uninstall await checkErrorRevert(extension.uninstall(), "ds-auth-unauthorized"); - await colony.uninstallExtension(TEST_EXTENSION, { from: ROOT }); + await colony.methods["uninstallExtension(address)"](extensionAddress, { from: ROOT }); const colonyBalance = await web3GetBalance(colony.address); expect(new BN(colonyBalance)).to.eq.BN(100); }); it("does not allow non-root users to uninstall an extension", async () => { - await checkErrorRevert(colony.uninstallExtension(TEST_EXTENSION, { from: ARCHITECT }), "ds-auth-unauthorized"); - - await checkErrorRevert(colony.uninstallExtension(TEST_EXTENSION, { from: USER }), "ds-auth-unauthorized"); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](ethers.constants.AddressZero, { from: USER }), "ds-auth-unauthorized"); }); it("does not allow root users to uninstall an extension which is not installed", async () => { - await checkErrorRevert(colony.uninstallExtension(TEST_EXTENSION, { from: ROOT }), "colony-network-extension-not-installed"); + await checkErrorRevert( + colony.methods["uninstallExtension(address)"](ethers.constants.AddressZero, { from: ROOT }), + "colony-network-extension-not-installed" + ); + }); + + it("allows old colonies to uninstall extensions correctly", async () => { + const version7Colony = await Version7.new(colonyNetwork.address); + + // Add version7Colony to _isColony mapping + const slot = soliditySha3(ethers.utils.hexZeroPad(version7Colony.address, 32), 19); + const value = ethers.utils.zeroPad(1, 32); + await editableColonyNetwork.setStorageSlot(slot, value); + + await version7Colony.installExtension(TEST_EXTENSION, 1); + + let extensionAddress; + extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, version7Colony.address); + expect(extensionAddress).to.not.equal(ethers.constants.AddressZero); + + await version7Colony.uninstallExtension(TEST_EXTENSION); + + extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_EXTENSION, version7Colony.address); + expect(extensionAddress).to.equal(ethers.constants.AddressZero); }); }); @@ -294,8 +424,9 @@ contract("Colony Network Extensions", (accounts) => { const tokenLockingAddress = await colonyNetwork.getTokenLocking(); const tokenLocking = await ITokenLocking.at(tokenLockingAddress); - await colony.installExtension(TEST_VOTING_TOKEN, 1, { from: ROOT }); - const testVotingTokenAddress = await colonyNetwork.getExtensionInstallation(TEST_VOTING_TOKEN, colony.address); + const tx = await colony.installExtension(TEST_VOTING_TOKEN, 1, { from: ROOT }); + + const testVotingTokenAddress = getExtensionAddressFromTx(tx); const testVotingToken = await TestVotingToken.at(testVotingTokenAddress); const lockCountPre = await tokenLocking.getTotalLockCount(token.address); @@ -329,16 +460,17 @@ contract("Colony Network Extensions", (accounts) => { }); it("does not allow users to lock and unlock tokens", async () => { - await checkErrorRevert(colony.lockToken(), "colony-sender-must-be-contract"); - await checkErrorRevert(colony.unlockTokenForUser(ROOT, 0), "colony-sender-must-be-contract"); + await checkErrorRevert(colony.lockToken(), "colony-must-be-extension"); + await checkErrorRevert(colony.unlockTokenForUser(ROOT, 0), "colony-must-be-extension"); }); it("does not allow a colony to unlock a lock placed by another colony", async () => { const tokenLockingAddress = await colonyNetwork.getTokenLocking(); const tokenLocking = await ITokenLocking.at(tokenLockingAddress); - await colony.installExtension(TEST_VOTING_TOKEN, 1, { from: ROOT }); - const testVotingTokenAddress = await colonyNetwork.getExtensionInstallation(TEST_VOTING_TOKEN, colony.address); + const tx = await colony.installExtension(TEST_VOTING_TOKEN, 1, { from: ROOT }); + + const testVotingTokenAddress = getExtensionAddressFromTx(tx); const testVotingToken = await TestVotingToken.at(testVotingTokenAddress); await testVotingToken.lockToken(); diff --git a/test/contracts-network/token-locking.js b/test/contracts-network/token-locking.js index 85bd76415a..ef3b22cd3c 100644 --- a/test/contracts-network/token-locking.js +++ b/test/contracts-network/token-locking.js @@ -6,12 +6,20 @@ import { ethers } from "ethers"; import { soliditySha3 } from "web3-utils"; import TruffleLoader from "../../packages/reputation-miner/TruffleLoader"; -import { getTokenArgs, checkErrorRevert, makeReputationKey, advanceMiningCycleNoContest, expectEvent } from "../../helpers/test-helper"; +import ReputationMinerTestWrapper from "../../packages/reputation-miner/test/ReputationMinerTestWrapper"; + import { giveUserCLNYTokensAndStake, setupColony, setupRandomColony, fundColonyWithTokens } from "../../helpers/test-data-generator"; import { UINT256_MAX, DEFAULT_STAKE } from "../../helpers/constants"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; -import ReputationMinerTestWrapper from "../../packages/reputation-miner/test/ReputationMinerTestWrapper"; +import { + getTokenArgs, + checkErrorRevert, + makeReputationKey, + advanceMiningCycleNoContest, + expectEvent, + getExtensionAddressFromTx, +} from "../../helpers/test-helper"; const { expect } = chai; chai.use(bnChai(web3.utils.BN)); @@ -331,8 +339,8 @@ contract("Token Locking", (addresses) => { await token.approve(tokenLocking.address, usersTokens, { from: userAddress }); await tokenLocking.methods["deposit(address,uint256,bool)"](token.address, usersTokens, true, { from: userAddress }); - await colony.installExtension(TEST_VOTING_TOKEN, 1); - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_VOTING_TOKEN, colony.address); + const tx = await colony.installExtension(TEST_VOTING_TOKEN, 1); + const extensionAddress = getExtensionAddressFromTx(tx); const votingToken = await TestVotingToken.at(extensionAddress); await votingToken.lockToken(); @@ -361,8 +369,8 @@ contract("Token Locking", (addresses) => { await token.approve(tokenLocking.address, usersTokens, { from: userAddress }); await tokenLocking.methods["deposit(address,uint256,bool)"](token.address, usersTokens, true, { from: userAddress }); - await colony.installExtension(TEST_VOTING_TOKEN, 1); - const extensionAddress = await colonyNetwork.getExtensionInstallation(TEST_VOTING_TOKEN, colony.address); + const tx = await colony.installExtension(TEST_VOTING_TOKEN, 1); + const extensionAddress = getExtensionAddressFromTx(tx); const votingToken = await TestVotingToken.at(extensionAddress); await votingToken.lockToken(); diff --git a/test/extensions/coin-machine.js b/test/extensions/coin-machine.js index 2b569dd177..7ecebe00b6 100644 --- a/test/extensions/coin-machine.js +++ b/test/extensions/coin-machine.js @@ -18,6 +18,7 @@ import { currentBlockTime, forwardTimeTo, expectEvent, + getExtensionAddressFromTx, } from "../../helpers/test-helper"; import { @@ -54,6 +55,14 @@ contract("Coin Machine", (accounts) => { const ADDRESS_ZERO = ethers.constants.AddressZero; + async function installCoinMachine() { + const tx = await colony.installExtension(COIN_MACHINE, coinMachineVersion); + const coinMachineAddress = getExtensionAddressFromTx(tx); + + coinMachine = await CoinMachine.at(coinMachineAddress); + return coinMachine; + } + before(async () => { colonyNetwork = await setupColonyNetwork(); const { metaColony } = await setupMetaColonyWithLockedCLNYToken(colonyNetwork); @@ -72,11 +81,7 @@ contract("Coin Machine", (accounts) => { beforeEach(async () => { ({ colony, token } = await setupRandomColony(colonyNetwork)); purchaseToken = await setupRandomToken(); - - await colony.installExtension(COIN_MACHINE, coinMachineVersion); - - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + coinMachine = await installCoinMachine(); // Forward time to start of a Coin Machine period - so long a test doesn't take an hour to run, should be reproducible! // (I still don't like this functionality of CoinMachine though!) @@ -109,16 +114,12 @@ contract("Coin Machine", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); + const tx = await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - await checkErrorRevert( - colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }), - "colony-network-extension-already-installed" - ); + const coinMachineAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](coinMachineAddress, { from: USER1 }), "ds-auth-unauthorized"); - await checkErrorRevert(colony.uninstallExtension(COIN_MACHINE, { from: USER1 }), "ds-auth-unauthorized"); - - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](coinMachineAddress, { from: USER0 }); }); it("can send unsold tokens back to the colony", async () => { @@ -126,7 +127,7 @@ contract("Coin Machine", (accounts) => { await coinMachine.initialise(token.address, ADDRESS_ZERO, 60 * 60, 10, WAD, WAD, WAD, WAD, ADDRESS_ZERO); - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); const balance = await token.balanceOf(colony.address); expect(balance).to.eq.BN(WAD); @@ -271,10 +272,8 @@ contract("Coin Machine", (accounts) => { const otherToken = await Token.new("", "", 18); await otherToken.unlock(); - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await otherToken.mint(coinMachine.address, UINT128_MAX); @@ -290,10 +289,8 @@ contract("Coin Machine", (accounts) => { }); it("cannot buy more than the balance of tokens", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await token.mint(coinMachine.address, WAD); @@ -319,8 +316,8 @@ contract("Coin Machine", (accounts) => { expect(locked).to.equal(true); colony = await setupColony(colonyNetwork, token.address); - await colony.installExtension(COIN_MACHINE, coinMachineVersion); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); + const tx = await colony.installExtension(COIN_MACHINE, coinMachineVersion); + const coinMachineAddress = getExtensionAddressFromTx(tx); coinMachine = await CoinMachine.at(coinMachineAddress); const tokenAuthority = await TokenAuthority.new(token.address, colony.address, [coinMachine.address]); @@ -346,7 +343,7 @@ contract("Coin Machine", (accounts) => { let deprecated = await coinMachine.getDeprecated(); expect(deprecated).to.equal(false); - await colony.deprecateExtension(COIN_MACHINE, true); + await colony.methods["deprecateExtension(address,bool)"](coinMachine.address, true); await checkErrorRevert(coinMachine.buyTokens(WAD, { from: USER0 }), "colony-extension-deprecated"); deprecated = await coinMachine.getDeprecated(); @@ -354,10 +351,8 @@ contract("Coin Machine", (accounts) => { }); it("can buy tokens with eth", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await token.mint(coinMachine.address, UINT128_MAX); @@ -461,9 +456,11 @@ contract("Coin Machine", (accounts) => { }); it("cannot adjust prices while the token balance is zero", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); + let tx; + + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + tx = await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); + const coinMachineAddress = getExtensionAddressFromTx(tx); coinMachine = await CoinMachine.at(coinMachineAddress); await token.mint(coinMachine.address, WAD.muln(200)); @@ -477,7 +474,6 @@ contract("Coin Machine", (accounts) => { let currentPrice; let evolvePrice; - let tx; await purchaseToken.mint(USER0, maxPerPeriod.muln(10), { from: USER0 }); await purchaseToken.approve(coinMachine.address, maxPerPeriod.muln(10), { from: USER0 }); @@ -539,7 +535,7 @@ contract("Coin Machine", (accounts) => { expect(currentPrice).to.eq.BN(WAD); await coinMachine.buyTokens(maxPerPeriod, { from: USER0 }); - tx = await colony.deprecateExtension(COIN_MACHINE, true, { from: USER0 }); + tx = await colony.methods["deprecateExtension(address,bool)"](coinMachine.address, true, { from: USER0 }); // Full event signature because we bounce the call through the colony await expectEvent(tx, "PriceEvolutionSet(bool)", [false]); @@ -555,7 +551,7 @@ contract("Coin Machine", (accounts) => { // Now we undeprecate the extension (and advance a period) // Price doesn't adjust in the period you update - await colony.deprecateExtension(COIN_MACHINE, false, { from: USER0 }); + tx = await colony.methods["deprecateExtension(address,bool)"](coinMachine.address, false, { from: USER0 }); await forwardTime(periodLength.toNumber(), this); tx = await coinMachine.updatePeriod(); @@ -706,9 +702,7 @@ contract("Coin Machine", (accounts) => { colony = await setupColony(colonyNetwork, token.address); await token.unlock(); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + coinMachine = await installCoinMachine(); await token.mint(coinMachine.address, UINT128_MAX); @@ -727,10 +721,8 @@ contract("Coin Machine", (accounts) => { purchaseToken = await Token.new("Test Token", "TEST", 9); await purchaseToken.unlock(); - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await token.mint(coinMachine.address, UINT128_MAX); @@ -797,10 +789,8 @@ contract("Coin Machine", (accounts) => { }); it("cannot buy more than their user limit allows", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await token.mint(coinMachine.address, WAD.muln(1000)); @@ -871,10 +861,8 @@ contract("Coin Machine", (accounts) => { }); it("cannot set a user limit without a whitelist", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); await coinMachine.initialise(token.address, purchaseToken.address, 60 * 60, 10, WAD.muln(100), WAD.muln(200), WAD.divn(2), WAD, ADDRESS_ZERO); @@ -883,10 +871,8 @@ contract("Coin Machine", (accounts) => { }); it("can calculate the max purchase at any given time", async () => { - await colony.uninstallExtension(COIN_MACHINE, { from: USER0 }); - await colony.installExtension(COIN_MACHINE, coinMachineVersion, { from: USER0 }); - const coinMachineAddress = await colonyNetwork.getExtensionInstallation(COIN_MACHINE, colony.address); - coinMachine = await CoinMachine.at(coinMachineAddress); + await colony.methods["uninstallExtension(address)"](coinMachine.address, { from: USER0 }); + coinMachine = await installCoinMachine(); // Initial supply of 250 tokens await token.mint(coinMachine.address, WAD.muln(250)); diff --git a/test/extensions/evaluated-expenditures.js b/test/extensions/evaluated-expenditures.js index 7bebe12aee..ffbba868ec 100644 --- a/test/extensions/evaluated-expenditures.js +++ b/test/extensions/evaluated-expenditures.js @@ -7,7 +7,7 @@ import { soliditySha3 } from "web3-utils"; import { UINT256_MAX, WAD } from "../../helpers/constants"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; -import { checkErrorRevert, web3GetCode } from "../../helpers/test-helper"; +import { checkErrorRevert, web3GetCode, getExtensionAddressFromTx } from "../../helpers/test-helper"; import { setupColonyNetwork, setupRandomColony, setupMetaColonyWithLockedCLNYToken } from "../../helpers/test-data-generator"; const { expect } = chai; @@ -46,9 +46,8 @@ contract("EvaluatedExpenditure", (accounts) => { beforeEach(async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(EVALUATED_EXPENDITURE, evaluatedExpenditureVersion); - - const evaluatedExpenditureAddress = await colonyNetwork.getExtensionInstallation(EVALUATED_EXPENDITURE, colony.address); + const tx = await colony.installExtension(EVALUATED_EXPENDITURE, evaluatedExpenditureVersion); + const evaluatedExpenditureAddress = getExtensionAddressFromTx(tx); evaluatedExpenditure = await EvaluatedExpenditure.at(evaluatedExpenditureAddress); await colony.setArbitrationRole(1, UINT256_MAX, evaluatedExpenditure.address, 1, true); @@ -79,16 +78,12 @@ contract("EvaluatedExpenditure", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(EVALUATED_EXPENDITURE, evaluatedExpenditureVersion, { from: USER0 }); - - await checkErrorRevert( - colony.installExtension(EVALUATED_EXPENDITURE, evaluatedExpenditureVersion, { from: USER0 }), - "colony-network-extension-already-installed" - ); + const tx = await colony.installExtension(EVALUATED_EXPENDITURE, evaluatedExpenditureVersion); - await checkErrorRevert(colony.uninstallExtension(EVALUATED_EXPENDITURE, { from: USER1 }), "ds-auth-unauthorized"); + const evaluatedExpenditureAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](evaluatedExpenditureAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(EVALUATED_EXPENDITURE, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](evaluatedExpenditureAddress, { from: USER0 }); }); }); diff --git a/test/extensions/funding-queue.js b/test/extensions/funding-queue.js index 86ef0211ec..c479f202d1 100644 --- a/test/extensions/funding-queue.js +++ b/test/extensions/funding-queue.js @@ -17,6 +17,7 @@ import { forwardTime, getBlockTime, removeSubdomainLimit, + getExtensionAddressFromTx, } from "../../helpers/test-helper"; import { @@ -112,9 +113,9 @@ contract("Funding Queues", (accounts) => { await colony.addDomain(1, 0, 2); domain1 = await colony.getDomain(1); domain2 = await colony.getDomain(2); - await colony.installExtension(FUNDING_QUEUE, fundingQueueVersion); - const fundingQueueAddress = await colonyNetwork.getExtensionInstallation(FUNDING_QUEUE, colony.address); + const tx = await colony.installExtension(FUNDING_QUEUE, fundingQueueVersion); + const fundingQueueAddress = getExtensionAddressFromTx(tx); fundingQueue = await FundingQueue.at(fundingQueueAddress); await colony.setFundingRole(1, UINT256_MAX, fundingQueue.address, 1, true); @@ -206,15 +207,12 @@ contract("Funding Queues", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(FUNDING_QUEUE, fundingQueueVersion, { from: USER0 }); + const tx = await colony.installExtension(FUNDING_QUEUE, fundingQueueVersion, { from: USER0 }); - await checkErrorRevert( - colony.installExtension(FUNDING_QUEUE, fundingQueueVersion, { from: USER0 }), - "colony-network-extension-already-installed" - ); - await checkErrorRevert(colony.uninstallExtension(FUNDING_QUEUE, { from: USER1 }), "ds-auth-unauthorized"); + const fundingQueueAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](fundingQueueAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(FUNDING_QUEUE, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](fundingQueueAddress, { from: USER0 }); }); }); @@ -232,7 +230,7 @@ contract("Funding Queues", (accounts) => { let deprecated = await fundingQueue.getDeprecated(); expect(deprecated).to.equal(false); - await colony.deprecateExtension(FUNDING_QUEUE, true); + await colony.methods["deprecateExtension(address,bool)"](fundingQueue.address, true); await checkErrorRevert( fundingQueue.createProposal(1, UINT256_MAX, 0, 1, 2, WAD, token.address, { from: USER0 }), diff --git a/test/extensions/one-tx-payment.js b/test/extensions/one-tx-payment.js index 65459f1d43..259df2c276 100644 --- a/test/extensions/one-tx-payment.js +++ b/test/extensions/one-tx-payment.js @@ -6,7 +6,7 @@ import { ethers } from "ethers"; import { soliditySha3 } from "web3-utils"; import { UINT256_MAX, WAD, INITIAL_FUNDING, GLOBAL_SKILL_ID, FUNDING_ROLE, ADMINISTRATION_ROLE } from "../../helpers/constants"; -import { checkErrorRevert, web3GetCode, rolesToBytes32, expectEvent } from "../../helpers/test-helper"; +import { checkErrorRevert, web3GetCode, rolesToBytes32, expectEvent, getExtensionAddressFromTx } from "../../helpers/test-helper"; import { setupColonyNetwork, setupMetaColonyWithLockedCLNYToken, setupRandomColony, fundColonyWithTokens } from "../../helpers/test-data-generator"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; @@ -57,8 +57,8 @@ contract("One transaction payments", (accounts) => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await colony.installExtension(ONE_TX_PAYMENT, oneTxPaymentVersion); - const oneTxPaymentAddress = await colonyNetwork.getExtensionInstallation(ONE_TX_PAYMENT, colony.address); + const tx = await colony.installExtension(ONE_TX_PAYMENT, oneTxPaymentVersion); + const oneTxPaymentAddress = getExtensionAddressFromTx(tx); oneTxPayment = await OneTxPayment.at(oneTxPaymentAddress); // Give extension funding and administration rights @@ -90,12 +90,12 @@ contract("One transaction payments", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(ONE_TX_PAYMENT, oneTxPaymentVersion); + const tx = await colony.installExtension(ONE_TX_PAYMENT, oneTxPaymentVersion); - await checkErrorRevert(colony.installExtension(ONE_TX_PAYMENT, oneTxPaymentVersion), "colony-network-extension-already-installed"); - await checkErrorRevert(colony.uninstallExtension(ONE_TX_PAYMENT, { from: USER1 }), "ds-auth-unauthorized"); + const oneTxPaymentAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](oneTxPaymentAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(ONE_TX_PAYMENT); + await colony.methods["uninstallExtension(address)"](oneTxPaymentAddress); }); }); diff --git a/test/extensions/token-supplier.js b/test/extensions/token-supplier.js index e21de7518c..13f26fda8c 100644 --- a/test/extensions/token-supplier.js +++ b/test/extensions/token-supplier.js @@ -7,10 +7,18 @@ import { ethers } from "ethers"; import { soliditySha3 } from "web3-utils"; import { UINT256_MAX, WAD, SECONDS_PER_DAY } from "../../helpers/constants"; -import { checkErrorRevert, currentBlockTime, makeTxAtTimestamp, getBlockTime, forwardTime } from "../../helpers/test-helper"; import { setupColonyNetwork, setupRandomColony, setupMetaColonyWithLockedCLNYToken } from "../../helpers/test-data-generator"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; +import { + checkErrorRevert, + currentBlockTime, + makeTxAtTimestamp, + getBlockTime, + forwardTime, + getExtensionAddressFromTx, +} from "../../helpers/test-helper"; + const { expect } = chai; chai.use(bnChai(web3.utils.BN)); @@ -50,10 +58,10 @@ contract("Token Supplier", (accounts) => { beforeEach(async () => { ({ colony, token } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(TOKEN_SUPPLIER, tokenSupplierVersion); await colony.addDomain(1, UINT256_MAX, 1); - const tokenSupplierAddress = await colonyNetwork.getExtensionInstallation(TOKEN_SUPPLIER, colony.address); + const tx = await colony.installExtension(TOKEN_SUPPLIER, tokenSupplierVersion); + const tokenSupplierAddress = getExtensionAddressFromTx(tx); tokenSupplier = await TokenSupplier.at(tokenSupplierAddress); await colony.setRootRole(tokenSupplier.address, true); @@ -76,15 +84,12 @@ contract("Token Supplier", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(TOKEN_SUPPLIER, tokenSupplierVersion, { from: USER0 }); + const tx = await colony.installExtension(TOKEN_SUPPLIER, tokenSupplierVersion, { from: USER0 }); - await checkErrorRevert( - colony.installExtension(TOKEN_SUPPLIER, tokenSupplierVersion, { from: USER0 }), - "colony-network-extension-already-installed" - ); - await checkErrorRevert(colony.uninstallExtension(TOKEN_SUPPLIER, { from: USER1 }), "ds-auth-unauthorized"); + const tokenSupplierAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](tokenSupplierAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(TOKEN_SUPPLIER, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](tokenSupplierAddress, { from: USER0 }); }); it("can initialise", async () => { diff --git a/test/extensions/voting-rep.js b/test/extensions/voting-rep.js index 75160d125d..0c8be8770e 100644 --- a/test/extensions/voting-rep.js +++ b/test/extensions/voting-rep.js @@ -19,6 +19,7 @@ import { encodeTxData, bn2bytes32, expectEvent, + getExtensionAddressFromTx, } from "../../helpers/test-helper"; import { @@ -143,8 +144,8 @@ contract("Voting Reputation", (accounts) => { domain2 = await colony.getDomain(2); domain3 = await colony.getDomain(3); - await colony.installExtension(VOTING_REPUTATION, reputationVotingVersion); - const votingAddress = await colonyNetwork.getExtensionInstallation(VOTING_REPUTATION, colony.address); + const tx = await colony.installExtension(VOTING_REPUTATION, reputationVotingVersion); + const votingAddress = getExtensionAddressFromTx(tx); voting = await VotingReputation.at(votingAddress); await voting.initialise( @@ -275,23 +276,20 @@ contract("Voting Reputation", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(VOTING_REPUTATION, reputationVotingVersion, { from: USER0 }); + const tx = await colony.installExtension(VOTING_REPUTATION, reputationVotingVersion, { from: USER0 }); - await checkErrorRevert( - colony.installExtension(VOTING_REPUTATION, reputationVotingVersion, { from: USER0 }), - "colony-network-extension-already-installed" - ); - await checkErrorRevert(colony.uninstallExtension(VOTING_REPUTATION, { from: USER1 }), "ds-auth-unauthorized"); + const votingAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](votingAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(VOTING_REPUTATION, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](votingAddress, { from: USER0 }); }); it("can deprecate the extension if root", async () => { let deprecated = await voting.getDeprecated(); expect(deprecated).to.equal(false); - await checkErrorRevert(colony.deprecateExtension(VOTING_REPUTATION, true, { from: USER2 }), "ds-auth-unauthorized"); - await colony.deprecateExtension(VOTING_REPUTATION, true); + await checkErrorRevert(colony.methods["deprecateExtension(address,bool)"](voting.address, true, { from: USER2 }), "ds-auth-unauthorized"); + await colony.methods["deprecateExtension(address,bool)"](voting.address, true, { from: USER0 }); // Can't make new motions! const action = await encodeTxData(colony, "mintTokens", [WAD]); @@ -1234,9 +1232,6 @@ contract("Voting Reputation", (accounts) => { }); it("can take an action to install an extension", async () => { - let installation = await colonyNetwork.getExtensionInstallation(soliditySha3("OneTxPayment"), colony.address); - expect(installation).to.be.equal(ADDRESS_ZERO); - const oneTxPaymentImplementation = await OneTxPayment.new(); const resolver = await Resolver.new(); await setupEtherRouter("OneTxPayment", { OneTxPayment: oneTxPaymentImplementation.address }, resolver); @@ -1252,11 +1247,12 @@ contract("Voting Reputation", (accounts) => { await forwardTime(STAKE_PERIOD, this); - const { logs } = await voting.finalizeMotion(motionId); - expect(logs[1].args.executed).to.be.true; + const tx = await voting.finalizeMotion(motionId); + expect(tx.logs[1].args.executed).to.be.true; - installation = await colonyNetwork.getExtensionInstallation(soliditySha3("OneTxPayment"), colony.address); - expect(installation).to.not.be.equal(ADDRESS_ZERO); + const extensionAddress = getExtensionAddressFromTx(tx); + const colonyAddress = await colonyNetwork.getExtensionColony(extensionAddress); + expect(colonyAddress).to.equal(colony.address); }); it("can take an action with an arbitrary target", async () => { diff --git a/test/extensions/whitelist.js b/test/extensions/whitelist.js index 93bdf83f1a..c18dfd4ac9 100644 --- a/test/extensions/whitelist.js +++ b/test/extensions/whitelist.js @@ -7,7 +7,7 @@ import { soliditySha3 } from "web3-utils"; import { UINT256_MAX, IPFS_HASH } from "../../helpers/constants"; import { setupEtherRouter } from "../../helpers/upgradable-contracts"; -import { checkErrorRevert, web3GetCode } from "../../helpers/test-helper"; +import { checkErrorRevert, web3GetCode, getExtensionAddressFromTx } from "../../helpers/test-helper"; import { setupColonyNetwork, setupRandomColony, setupMetaColonyWithLockedCLNYToken } from "../../helpers/test-data-generator"; const { expect } = chai; @@ -46,9 +46,8 @@ contract("Whitelist", (accounts) => { beforeEach(async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(WHITELIST, whitelistVersion); - - const whitelistAddress = await colonyNetwork.getExtensionInstallation(WHITELIST, colony.address); + const tx = await colony.installExtension(WHITELIST, whitelistVersion); + const whitelistAddress = getExtensionAddressFromTx(tx); whitelist = await Whitelist.at(whitelistAddress); await colony.setAdministrationRole(1, UINT256_MAX, USER0, 1, true); @@ -79,12 +78,12 @@ contract("Whitelist", (accounts) => { it("can install the extension with the extension manager", async () => { ({ colony } = await setupRandomColony(colonyNetwork)); - await colony.installExtension(WHITELIST, whitelistVersion, { from: USER0 }); + const tx = await colony.installExtension(WHITELIST, whitelistVersion, { from: USER0 }); - await checkErrorRevert(colony.installExtension(WHITELIST, whitelistVersion, { from: USER0 }), "colony-network-extension-already-installed"); - await checkErrorRevert(colony.uninstallExtension(WHITELIST, { from: USER1 }), "ds-auth-unauthorized"); + const whitelistAddress = getExtensionAddressFromTx(tx); + await checkErrorRevert(colony.methods["uninstallExtension(address)"](whitelistAddress, { from: USER1 }), "ds-auth-unauthorized"); - await colony.uninstallExtension(WHITELIST, { from: USER0 }); + await colony.methods["uninstallExtension(address)"](whitelistAddress, { from: USER0 }); }); }); @@ -221,7 +220,7 @@ contract("Whitelist", (accounts) => { status = await whitelist.isApproved(USER1); expect(status).to.be.true; - await colony.deprecateExtension(WHITELIST, true, { from: USER0 }); + await colony.methods["deprecateExtension(address,bool)"](whitelist.address, true, { from: USER0 }); status = await whitelist.isApproved(USER1); expect(status).to.be.false;