diff --git a/src/index.ts b/src/index.ts index d571f779..28de3ffe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,9 +67,14 @@ import { encodeValueContent, decodeValueContent, } from './lib/encoder'; -import { internalSupportsInterface, checkPermissions } from './lib/detector'; +import { internalSupportsInterface } from './lib/detector'; import { decodeMappingKey } from './lib/decodeMappingKey'; -import { encodePermissions, decodePermissions } from './lib/permissions'; +import { + encodePermissions, + decodePermissions, + checkPermissions, + mapPermission, +} from './lib/permissions'; import { AssetURLEncode } from './types/encodeData'; import { URLDataToEncode, URLDataWithHash, Verification } from './types'; @@ -95,8 +100,12 @@ export { decodeValueContent, } from './lib/encoder'; export { getDataFromExternalSources } from './lib/getDataFromExternalSources'; -export { encodePermissions, decodePermissions } from './lib/permissions'; -export { checkPermissions } from './lib/detector'; +export { + encodePermissions, + decodePermissions, + checkPermissions, + mapPermission, +} from './lib/permissions'; export { getSchema } from './lib/schemaParser'; // PRIVATE FUNCTION @@ -502,50 +511,6 @@ export class ERC725 { ); } - /** - * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. - * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. - */ - static encodePermissions(permissions: Permissions): string { - return encodePermissions(permissions); - } - - /** - * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. - * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. - */ - encodePermissions(permissions: Permissions): string { - return encodePermissions(permissions); - } - - /** - * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissionHex The permission hexadecimal value to be decoded. - * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. - */ - static decodePermissions(permissionHex: string) { - return decodePermissions(permissionHex); - } - - /** - * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. - * - * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. - * @param permissionHex The permission hexadecimal value to be decoded. - * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. - */ - decodePermissions(permissionHex: string) { - return decodePermissions(permissionHex); - } - /** * Hashes a key name for use on an ERC725Y contract according to LSP2 ERC725Y JSONSchema standard. * @@ -634,6 +599,53 @@ export class ERC725 { return supportsInterface(interfaceIdOrName, options); } + // Permissions related functions + // ----------------------------- + + /** + * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. + * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. + */ + static encodePermissions(permissions: Permissions): string { + return encodePermissions(permissions); + } + + /** + * Encode permissions into a hexadecimal string as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissions The permissions you want to specify to be included or excluded. Any ommitted permissions will default to false. + * @returns {*} The permissions encoded as a hexadecimal string as defined by the LSP6 Standard. + */ + encodePermissions(permissions: Permissions): string { + return encodePermissions(permissions); + } + + /** + * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissionHex The permission hexadecimal value to be decoded. + * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. + */ + static decodePermissions(permissionHex: string) { + return decodePermissions(permissionHex); + } + + /** + * Decodes permissions from hexadecimal as defined by the LSP6 KeyManager Standard. + * + * @link https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md LSP6 KeyManager Standard. + * @param permissionHex The permission hexadecimal value to be decoded. + * @returns Object specifying whether default LSP6 permissions are included in provided hexademical string. + */ + decodePermissions(permissionHex: string) { + return decodePermissions(permissionHex); + } + /** * Check if the required permissions are included in the granted permissions as defined by the LSP6 KeyManager Standard. * @@ -664,6 +676,14 @@ export class ERC725 { return checkPermissions(requiredPermissions, grantedPermissions); } + static mapPermission(permission: string): string | null { + return mapPermission(permission); + } + + mapPermission(permission: string): string | null { + return mapPermission(permission); + } + encodeDataSourceWithHash( verification: undefined | Verification, dataSource: string, diff --git a/src/lib/detector.test.ts b/src/lib/detector.test.ts index d397feae..ac4afd50 100644 --- a/src/lib/detector.test.ts +++ b/src/lib/detector.test.ts @@ -24,7 +24,7 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import { INTERFACE_IDS_0_12_0 } from '../constants/interfaces'; -import { internalSupportsInterface, checkPermissions } from './detector'; +import { internalSupportsInterface } from './detector'; describe('supportsInterface', () => { it('it should return true if the contract supports the interface with name', async () => { @@ -66,165 +66,3 @@ describe('supportsInterface', () => { expect(doesSupportInterface).to.be.true; }); }); - -describe('checkPermissions', () => { - describe('test with single permission', () => { - it('should throw an error when given an invalid permission string', async () => { - const requiredPermissions = 'INVALIDPERMISSION'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an invalid 32-byte hex string', async () => { - const requiredPermissions = '0xinvalidhexstring'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an invalid grantedPermission 32-byte hex string', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = '0xinvalidgrantedpermissionhexstring'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', - ); - }); - - it('should return true when single literal permission matches granted permissions', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return true when single bytes32 permission matches granted permissions', async () => { - const requiredPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000001'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when single bytes32 permission does not match granted permissions', async () => { - const requiredPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000001'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000fff2'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when single literal permission does not match granted permissions', async () => { - const requiredPermissions = 'CHANGEOWNER'; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000fff2'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - }); - - describe('test with multiple permissions', () => { - it('should throw an error when given an array containing an invalid permission string', async () => { - const requiredPermissions = ['CHANGEOWNER', 'INVALIDPERMISSION']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should throw an error when given an array containing an invalid 32-byte hex string', async () => { - const requiredPermissions = ['CHANGEOWNER', '0xinvalidhexstring']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - expect(() => - checkPermissions(requiredPermissions, grantedPermissions), - ).to.throw( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - }); - - it('should return false when one of the literal permissions does not match granted permissions', async () => { - const requiredPermissions = ['EDITPERMISSIONS', 'CALL']; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when one of the bytes32 permissions does not match granted permissions', async () => { - const requiredPermissions = [ - '0x0000000000000000000000000000000000000000000000000000000000000004', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff51'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return true when all the mixed literal and bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - 'EDITPERMISSIONS', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x000000000000000000000000000000000000000000000000000000000000ff54'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when not all multiple literal permissions match granted permissions', async () => { - const requiredPermissions = ['CHANGEOWNER', 'CALL']; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return true when all multiple literal permissions match granted permissions', async () => { - const requiredPermissions = ['CHANGEOWNER', 'CALL']; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000801'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.true; - }); - - it('should return false when not all multiple bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - '0x0000000000000000000000000000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - - it('should return false when not all mixed literal and bytes32 permissions match granted permissions', async () => { - const requiredPermissions = [ - 'CHANGEOWNER', - '0x0000000000000000000000000000000000000000000000000000000000000800', - ]; - const grantedPermissions = - '0x0000000000000000000000000000000000000000000000000000000000000051'; - const result = checkPermissions(requiredPermissions, grantedPermissions); - expect(result).to.be.false; - }); - }); -}); diff --git a/src/lib/detector.ts b/src/lib/detector.ts index 1fae16a1..e7a6e1b9 100644 --- a/src/lib/detector.ts +++ b/src/lib/detector.ts @@ -21,8 +21,6 @@ * @date 2022 */ -import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants'; - import { AddressProviderOptions, INTERFACE_IDS_0_12_0, @@ -56,76 +54,3 @@ export const internalSupportsInterface = async ( throw new Error(`Error checking the interface: ${error}`); } }; - -/** - * @notice Check if the given string is a valid 32-byte hex string. - * @param str The string to be checked. - * @return A boolean value indicating whether the string is a valid 32-byte hex string. - */ -function isValid32ByteHexString(str: string): boolean { - return ( - str.startsWith('0x') && - str.length === 66 && - str - .slice(2) - .split('') - .every((char) => '0123456789abcdefABCDEF'.includes(char)) - ); -} - -/** - * @notice Map a permission to its corresponding bytes32 representation. - * @param permission The permission string to be mapped. - * @return The bytes32 representation of the permission. - * @dev Throws an error if the input is not a known permission name or a valid 32-byte hex string. - */ -function mapPermission(permission: string): string { - if ( - !LSP6_DEFAULT_PERMISSIONS[permission] && - !isValid32ByteHexString(permission) - ) { - throw new Error( - 'Invalid permission string. It must be a valid 32-byte hex string or a known permission name.', - ); - } - return LSP6_DEFAULT_PERMISSIONS[permission] || permission; -} - -/** - * @notice Check if the required permissions are included in the granted permissions. - * @param requiredPermissions An array of required permissions or a single required permission. - * @param grantedPermissions The granted permissions as a 32-byte hex string. - * @return A boolean value indicating whether the required permissions are included in the granted permissions. - * @dev Throws an error if the grantedPermissions input is not a valid 32-byte hex string. - */ -export const checkPermissions = ( - requiredPermissions: string[] | string, - grantedPermissions: string, -): boolean => { - // Validate the grantedPermissions string - if (!isValid32ByteHexString(grantedPermissions)) { - throw new Error( - 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', - ); - } - - // Convert requiredPermissions to an array if it's a single string - const requiredPermissionArray: string[] = Array.isArray(requiredPermissions) - ? requiredPermissions - : [requiredPermissions]; - - // Map the literal permissions to their bytes32 representation - const mappedPermissionArray: string[] = - requiredPermissionArray.map(mapPermission); - - // Perform the AND operation check for each required permission - return mappedPermissionArray.every((requiredPermission: string) => { - const requiredPermissionBigInt = BigInt(requiredPermission); - const grantedPermissionsBigInt = BigInt(grantedPermissions); - - return ( - (requiredPermissionBigInt & grantedPermissionsBigInt) === - requiredPermissionBigInt - ); - }); -}; diff --git a/src/lib/encoder.test.ts b/src/lib/encoder.test.ts index 4f8d02b5..aa6b62f0 100644 --- a/src/lib/encoder.test.ts +++ b/src/lib/encoder.test.ts @@ -409,6 +409,11 @@ describe('encoder', () => { decodedValue: 11, encodedValue: '0x0000000000000000000000000000000b', }, + { + valueType: 'uint128', + decodedValue: 0, + encodedValue: '0x00000000000000000000000000000000', + }, ]; validTestCases.forEach((testCase) => { diff --git a/src/lib/permissions.test.ts b/src/lib/permissions.test.ts new file mode 100644 index 00000000..f3822f3a --- /dev/null +++ b/src/lib/permissions.test.ts @@ -0,0 +1,164 @@ +import { expect } from 'chai'; +import { checkPermissions } from './permissions'; + +describe('checkPermissions', () => { + describe('test with single permission', () => { + it('should throw an error when given an invalid permission string', async () => { + const requiredPermissions = 'INVALIDPERMISSION'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + `Invalid permission string: ${requiredPermissions}. It must be a valid 32-byte hex string or a known permission name.`, + ); + }); + + it('should throw an error when given an invalid 32-byte hex string', async () => { + const requiredPermissions = '0xinvalidhexstring'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + `Invalid permission string: ${requiredPermissions}. It must be a valid 32-byte hex string or a known permission name.`, + ); + }); + + it('should throw an error when given an invalid grantedPermission 32-byte hex string', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = '0xinvalidgrantedpermissionhexstring'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', + ); + }); + + it('should return true when single literal permission matches granted permissions', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return true when single bytes32 permission matches granted permissions', async () => { + const requiredPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000001'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when single bytes32 permission does not match granted permissions', async () => { + const requiredPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000001'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000fff2'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when single literal permission does not match granted permissions', async () => { + const requiredPermissions = 'CHANGEOWNER'; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000fff2'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + }); + + describe('test with multiple permissions', () => { + it('should throw an error when given an array containing an invalid permission string', async () => { + const requiredPermissions = ['CHANGEOWNER', 'INVALIDPERMISSION']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + `Invalid permission string: ${requiredPermissions[1]}. It must be a valid 32-byte hex string or a known permission name.`, + ); + }); + + it('should throw an error when given an array containing an invalid 32-byte hex string', async () => { + const requiredPermissions = ['CHANGEOWNER', '0xinvalidhexstring']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + expect(() => + checkPermissions(requiredPermissions, grantedPermissions), + ).to.throw( + `Invalid permission string: ${requiredPermissions[1]}. It must be a valid 32-byte hex string or a known permission name.`, + ); + }); + + it('should return false when one of the literal permissions does not match granted permissions', async () => { + const requiredPermissions = ['EDITPERMISSIONS', 'CALL']; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when one of the bytes32 permissions does not match granted permissions', async () => { + const requiredPermissions = [ + '0x0000000000000000000000000000000000000000000000000000000000000004', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff51'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return true when all the mixed literal and bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + 'EDITPERMISSIONS', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x000000000000000000000000000000000000000000000000000000000000ff54'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when not all multiple literal permissions match granted permissions', async () => { + const requiredPermissions = ['CHANGEOWNER', 'CALL']; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return true when all multiple literal permissions match granted permissions', async () => { + const requiredPermissions = ['CHANGEOWNER', 'CALL']; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000801'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.true; + }); + + it('should return false when not all multiple bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + '0x0000000000000000000000000000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + + it('should return false when not all mixed literal and bytes32 permissions match granted permissions', async () => { + const requiredPermissions = [ + 'CHANGEOWNER', + '0x0000000000000000000000000000000000000000000000000000000000000800', + ]; + const grantedPermissions = + '0x0000000000000000000000000000000000000000000000000000000000000051'; + const result = checkPermissions(requiredPermissions, grantedPermissions); + expect(result).to.be.false; + }); + }); +}); diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts index b35fb55e..917791d5 100644 --- a/src/lib/permissions.ts +++ b/src/lib/permissions.ts @@ -1,4 +1,4 @@ -import { hexToNumber, leftPad, numberToHex } from 'web3-utils'; +import { hexToNumber, isHexStrict, leftPad, numberToHex } from 'web3-utils'; import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants'; import { Permissions } from '../types/Method'; @@ -80,3 +80,62 @@ export function decodePermissions(permissionHex: string) { return result; } + +/** + * @notice Map a permission to its corresponding bytes32 representation. + * @param permission The permission string to be mapped. + * @return The bytes32 representation of the permission. + * @dev Return null if the input is not a known permission name or a valid 32-byte hex string. + */ +export function mapPermission(permission: string): string | null { + if (!LSP6_DEFAULT_PERMISSIONS[permission] && !isHexStrict(permission)) { + return null; + } + return LSP6_DEFAULT_PERMISSIONS[permission] || permission; +} + +/** + * @notice Check if the required permissions are included in the granted permissions. + * @param requiredPermissions An array of required permissions or a single required permission. + * @param grantedPermissions The granted permissions as a 32-byte hex string. + * @return A boolean value indicating whether the required permissions are included in the granted permissions. + * @dev Throws an error if the grantedPermissions input is not a valid 32-byte hex string. + */ +export const checkPermissions = ( + requiredPermissions: string[] | string, + grantedPermissions: string, +): boolean => { + // Validate the grantedPermissions string + if (!isHexStrict(grantedPermissions)) { + throw new Error( + 'Invalid grantedPermissions string. It must be a valid 32-byte hex string.', + ); + } + + // Convert requiredPermissions to an array if it's a single string + const requiredPermissionArray: string[] = Array.isArray(requiredPermissions) + ? requiredPermissions + : [requiredPermissions]; + + // Map the literal permissions to their bytes32 representation + const mappedPermissionArray: (string | null)[] = + requiredPermissionArray.map(mapPermission); + + // Perform the AND operation check for each required permission + return mappedPermissionArray.every( + (requiredPermission: string | null, index: number) => { + if (!requiredPermission) { + throw new Error( + `Invalid permission string: ${requiredPermissionArray[index]}. It must be a valid 32-byte hex string or a known permission name.`, + ); + } + const requiredPermissionBigInt = BigInt(requiredPermission); + const grantedPermissionsBigInt = BigInt(grantedPermissions); + + return ( + (requiredPermissionBigInt & grantedPermissionsBigInt) === + requiredPermissionBigInt + ); + }, + ); +};