diff --git a/contracts/adapters/Manager.sol b/contracts/adapters/Manager.sol index e63464dab..d8ba92973 100644 --- a/contracts/adapters/Manager.sol +++ b/contracts/adapters/Manager.sol @@ -162,12 +162,40 @@ contract Manager is Reimbursable, AdapterGuard, Signatures { proposal.keys, proposal.values ); + + // Grant new adapter access to extensions. + for (uint256 i = 0; i < proposal.extensionAclFlags.length; i++) { + _grantExtensionAccess( + dao, + proposal.extensionAddresses[i], + proposal.adapterOrExtensionAddr, + proposal.extensionAclFlags[i] + ); + } } else if (proposal.updateType == UpdateType.EXTENSION) { _replaceExtension(dao, proposal); - } else if (proposal.updateType != UpdateType.CONFIGS) { + + // Grant adapters access to new extension. + for (uint256 i = 0; i < proposal.extensionAclFlags.length; i++) { + _grantExtensionAccess( + dao, + proposal.adapterOrExtensionAddr, + proposal.extensionAddresses[i], // Adapters. + proposal.extensionAclFlags[i] + ); + } + } else if (proposal.updateType == UpdateType.CONFIGS) { + for (uint256 i = 0; i < proposal.extensionAclFlags.length; i++) { + _grantExtensionAccess( + dao, + proposal.extensionAddresses[i], + proposal.adapterOrExtensionAddr, + proposal.extensionAclFlags[i] + ); + } + } else { revert("unknown update type"); } - _grantExtensionAccess(dao, proposal); _saveDaoConfigurations(dao, configs); } @@ -206,22 +234,22 @@ contract Manager is Reimbursable, AdapterGuard, Signatures { */ function _grantExtensionAccess( DaoRegistry dao, - ProposalDetails memory proposal + address extensionAddr, + address adapterAddr, + uint128 acl ) internal { - for (uint256 i = 0; i < proposal.extensionAclFlags.length; i++) { - // It is fine to execute the external call inside the loop - // because it is calling the correct function in the dao contract - // it won't be calling a fallback that always revert. - // slither-disable-next-line calls-loop - dao.setAclToExtensionForAdapter( - // It needs to be registered as extension - proposal.extensionAddresses[i], - // It needs to be registered as adapter - proposal.adapterOrExtensionAddr, - // Indicate which access level will be granted - proposal.extensionAclFlags[i] - ); - } + // It is fine to execute the external call inside the loop + // because it is calling the correct function in the dao contract + // it won't be calling a fallback that always revert. + // slither-disable-next-line calls-loop + dao.setAclToExtensionForAdapter( + // It needs to be registered as extension + extensionAddr, + // It needs to be registered as adapter + adapterAddr, + // Indicate which access level will be granted + acl + ); } /** diff --git a/package-lock.json b/package-lock.json index 7d7cec1f0..426dc8b89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "tribute-contracts", - "version": "2.5.0", + "version": "2.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "2.5.0", + "version": "2.6.0", "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/package.json b/package.json index 59a6a1e45..5d6f5cf99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tribute-contracts", - "version": "2.5.0", + "version": "2.6.0", "description": "A new modular DAO framework, inspired by the Moloch smart contracts", "keywords": [ "dao", diff --git a/test/adapters/manager.test.js b/test/adapters/manager.test.js index fb759d7bb..9c29c916a 100644 --- a/test/adapters/manager.test.js +++ b/test/adapters/manager.test.js @@ -44,6 +44,7 @@ const { web3, Manager, FinancingContract, + BankExtension, ERC1271Extension, NFTExtension, } = require("../../utils/hardhat-test-util"); @@ -1152,4 +1153,94 @@ describe("Adapter - Manager", () => { ) ).equal(true); }); + + it("should be possible to grant adapters access when replacing extension", async () => { + const dao = this.dao; + const manager = this.adapters.manager; + const kycOnboarding = this.adapters.kycOnboarding; + const newBank = await BankExtension.new(); + const nonce = (await manager.nonces(dao.address)).toNumber() + 1; + const proposal = { + adapterOrExtensionId: sha3("bank"), + adapterOrExtensionAddr: newBank.address, + updateType: 2, // UpdateType 3 = configs + flags: 0, + keys: [], + values: [], + extensionAddresses: [manager.address, kycOnboarding.address], + extensionAclFlags: [ + entryBank(manager.address, { + extensions: { + [extensionsIdsMap.BANK_EXT]: [ + bankExtensionAclFlagsMap.ADD_TO_BALANCE, + bankExtensionAclFlagsMap.SUB_FROM_BALANCE, + bankExtensionAclFlagsMap.INTERNAL_TRANSFER, + ], + }, + }).flags, + entryBank(kycOnboarding.address, { + extensions: { + [extensionsIdsMap.BANK_EXT]: [ + bankExtensionAclFlagsMap.ADD_TO_BALANCE, + bankExtensionAclFlagsMap.INTERNAL_TRANSFER, + ], + }, + }).flags, + ], + }; + const configs = []; + + const signature = generateCouponSignature({ + daoAddress: dao.address, + managerAddress: manager.address, + chainId, + proposal, + configs, + nonce, + }); + + await manager.processSignedProposal( + dao.address, + proposal, + configs, + nonce, + signature + ); + + expect( + await dao.hasAdapterAccessToExtension( + manager.address, + newBank.address, + 0 //ADD_TO_BALANCE + ) + ).equal(true); + expect( + await dao.hasAdapterAccessToExtension( + manager.address, + newBank.address, + 1 // SUB_FROM_BALANCE + ) + ).equal(true); + expect( + await dao.hasAdapterAccessToExtension( + manager.address, + newBank.address, + 2 // INTERNAL_TRANSFER + ) + ).equal(true); + expect( + await dao.hasAdapterAccessToExtension( + kycOnboarding.address, + newBank.address, + 0 //ADD_TO_BALANCE + ) + ).equal(true); + expect( + await dao.hasAdapterAccessToExtension( + kycOnboarding.address, + newBank.address, + 2 // INTERNAL_TRANSFER + ) + ).equal(true); + }); });