Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ interface IRoleManagement {
*/
error ArraysLengthNotEqual(uint256 lengthArray1, uint256 lengthArray2);

// emitted when trying to remove all admin accounts
error NoAdminsLeft();

/**
* @dev Grant the provided "roles" to all the "accounts", if CASHIN then "amounts" are the allowances
*
Expand Down
2 changes: 2 additions & 0 deletions contracts/contracts/extensions/RolesStorageWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {IRoles} from './Interfaces/IRoles.sol';
// solhint-disable-next-line max-line-length
import {ADMIN_ROLE, _CASHIN_ROLE, _BURN_ROLE, _WIPE_ROLE, _RESCUE_ROLE, _PAUSE_ROLE, _FREEZE_ROLE, _DELETE_ROLE, _WITHOUT_ROLE, _KYC_ROLE, _CUSTOM_FEES_ROLE, _HOLD_CREATOR_ROLE} from '../constants/roles.sol';
import {_ROLES_STORAGE_POSITION} from '../constants/storagePositions.sol';
import {IRoleManagement} from './Interfaces/IRoleManagement.sol';

abstract contract RolesStorageWrapper {
struct MemberData {
Expand Down Expand Up @@ -123,6 +124,7 @@ abstract contract RolesStorageWrapper {
rolesStorage.roles[role].accounts.pop();
delete (rolesStorage.roles[role].members[account]);
emit RoleRevoked(role, account, msg.sender);
if (_getNumberOfAccountsWithRole(_getRoleId(IRoles.RoleName.ADMIN)) == 0) revert IRoleManagement.NoAdminsLeft();
}

function _getAccountsWithRole(bytes32 role) internal view returns (address[] memory) {
Expand Down
23 changes: 23 additions & 0 deletions contracts/test/thread0/roles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,29 @@ describe('➡️ Roles Tests', function () {
})
expect(hasBurnRole).to.equals(false)
})

it('Can not revoke all admin role from a token', async function () {
const Admins = await rolesFacet.getAccountsWithRole(ROLES.defaultAdmin.hash, {
gasLimit: GAS_LIMIT.hederaTokenManager.getAccountsWithRole,
})

const Length = Admins.length

for (let i = 0; i < Length - 1; i++) {
await rolesFacet.revokeRole(ROLES.defaultAdmin.hash, Admins[i], {
gasLimit: GAS_LIMIT.hederaTokenManager.revokeRole,
})
}

const revokeRoleResponse = await rolesFacet.revokeRole(ROLES.defaultAdmin.hash, Admins[Length - 1], {
gasLimit: GAS_LIMIT.hederaTokenManager.revokeRole,
})
await expect(
new ValidateTxResponseCommand({
txResponse: revokeRoleResponse,
}).execute()
).to.be.rejectedWith(Error)
})
// * Initial State again

it('Getting roles', async function () {
Expand Down
19 changes: 19 additions & 0 deletions contracts/test/thread1/roleManagement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,25 @@ describe('➡️ Role Management Tests', function () {
await expect(new ValidateTxResponseCommand({ txResponse }).execute()).to.be.rejectedWith(Error)
})

it('Can not revoke all admin role from a token', async function () {
// Non operator has burn role
const Admins = await rolesFacet.getAccountsWithRole(ROLES.defaultAdmin.hash, {
gasLimit: GAS_LIMIT.hederaTokenManager.getAccountsWithRole,
})

const adminAccounts: string[] = []

for (let i = 0; i < Admins.length; i++) {
adminAccounts.push(Admins[i])
}

const txResponse = await roleManagementFacet.revokeRoles([ROLES.defaultAdmin.hash], adminAccounts, {
gasLimit: GAS_LIMIT.hederaTokenManager.revokeRoles,
})

await expect(new ValidateTxResponseCommand({ txResponse }).execute()).to.be.rejectedWith(Error)
})

it('An account can get all roles of any account', async function () {
// Grant roles
const Roles = [ROLES.burn.hash]
Expand Down
Loading