From d2b11aef765c398b24f9fd75d119a796309d1c97 Mon Sep 17 00:00:00 2001 From: Alberto Molina Date: Thu, 9 Oct 2025 16:28:42 +0200 Subject: [PATCH 1/4] fix: grantRoles does not accept amount as 0 anymore Signed-off-by: Alberto Molina --- .../extensions/Interfaces/IRoleManagement.sol | 3 ++ .../extensions/RoleManagementFacet.sol | 5 +-- contracts/scripts/constants.ts | 1 + contracts/test/thread1/roleManagement.test.ts | 34 +++++++++++++++++-- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/contracts/contracts/extensions/Interfaces/IRoleManagement.sol b/contracts/contracts/extensions/Interfaces/IRoleManagement.sol index 3010a17c6..65f9612e8 100644 --- a/contracts/contracts/extensions/Interfaces/IRoleManagement.sol +++ b/contracts/contracts/extensions/Interfaces/IRoleManagement.sol @@ -13,6 +13,9 @@ interface IRoleManagement { // emitted when trying to remove all admin accounts error NoAdminsLeft(); + // emitted when assigning supplier role with 0 amount + error AmountIsZero(); + /** * @dev Grant the provided "roles" to all the "accounts", if CASHIN then "amounts" are the allowances * diff --git a/contracts/contracts/extensions/RoleManagementFacet.sol b/contracts/contracts/extensions/RoleManagementFacet.sol index 42e61a3db..05f21fabb 100644 --- a/contracts/contracts/extensions/RoleManagementFacet.sol +++ b/contracts/contracts/extensions/RoleManagementFacet.sol @@ -26,8 +26,9 @@ contract RoleManagementFacet is IRoleManagement, IStaticFunctionSelectors, Suppl if (roles[i] == cashInRole) { if (accounts.length != amounts.length) revert ArraysLengthNotEqual(accounts.length, amounts.length); for (uint256 j = 0; j < accounts.length; j++) { - if (amounts[j] != 0) _grantSupplierRole(accounts[j], amounts[j]); - else _grantUnlimitedSupplierRole(accounts[j]); + if (amounts[j] == 0) revert AmountIsZero(); + if (amounts[j] == type(uint256).max) _grantUnlimitedSupplierRole(accounts[j]); + else _grantSupplierRole(accounts[j], amounts[j]); } } else { for (uint256 p = 0; p < accounts.length; p++) { diff --git a/contracts/scripts/constants.ts b/contracts/scripts/constants.ts index 4dc7cc4c5..9f0410b0d 100644 --- a/contracts/scripts/constants.ts +++ b/contracts/scripts/constants.ts @@ -12,6 +12,7 @@ export const DEFAULT_CONFIG_VERSION = 1 // * Ethereum export const ADDRESS_ZERO = ZeroAddress export const NUMBER_ZERO = 0n +export const UINT256_MAX = (1n << 256n) - 1n // * Hedera export const HBAR_DECIMALS = 8n diff --git a/contracts/test/thread1/roleManagement.test.ts b/contracts/test/thread1/roleManagement.test.ts index affbf1b2d..06d32c046 100644 --- a/contracts/test/thread1/roleManagement.test.ts +++ b/contracts/test/thread1/roleManagement.test.ts @@ -16,6 +16,7 @@ import { DeployFullInfrastructureCommand, MESSAGES, ROLES, + UINT256_MAX, ValidateTxResponseCommand, } from '@scripts' import { deployStableCoinInTests, GAS_LIMIT, randomAccountAddressList } from '@test/shared' @@ -121,7 +122,7 @@ describe('➡️ Role Management Tests', function () { const txResponse = await roleManagementFacet.grantRoles( rolesToGrant, randomAccountList, - randomAccountList.map((_, index) => toBigInt(index)), + randomAccountList.map((_, index) => toBigInt(index + 1)), { gasLimit: GAS_LIMIT.hederaTokenManager.grantRoles, } @@ -142,14 +143,14 @@ describe('➡️ Role Management Tests', function () { gasLimit: GAS_LIMIT.hederaTokenManager.getSupplierAllowance, }) - expect(allowance.toString()).to.eq(accountIndex.toString()) + expect(allowance.toString()).to.eq((accountIndex + 1).toString()) const isUnlimited = await supplierAdminFacet .connect(nonOperator) .isUnlimitedSupplierAllowance(randomAccountList[accountIndex], { gasLimit: GAS_LIMIT.hederaTokenManager.isUnlimitedSupplierAllowance, }) - expect(isUnlimited).to.eq(accountIndex == 0) + expect(isUnlimited).to.eq(false) } }) @@ -317,4 +318,31 @@ describe('➡️ Role Management Tests', function () { roleManagementFacet.grantRoles([ROLES.burn.hash], listOfAccounts, []) ).to.be.revertedWithCustomError(roleManagementFacet, 'AddressZero') }) + + it('Granting CashInRole with 0 amount fails', async function () { + await expect( + roleManagementFacet.grantRoles( + [ROLES.cashin.hash], + randomAccountList, + randomAccountList.map(() => 0) + ) + ).to.be.revertedWithCustomError(roleManagementFacet, 'AmountIsZero') + }) + + it('Granting CashInRole with 0 amount fails', async function () { + await roleManagementFacet.grantRoles( + [ROLES.cashin.hash], + randomAccountList, + randomAccountList.map(() => UINT256_MAX) + ) + + for (let i = 0; i < randomAccountList.length; i++) { + const isUnlimited = await supplierAdminFacet + .connect(nonOperator) + .isUnlimitedSupplierAllowance(randomAccountList[i], { + gasLimit: GAS_LIMIT.hederaTokenManager.isUnlimitedSupplierAllowance, + }) + expect(isUnlimited).to.eq(true) + } + }) }) From c8f64d3e4caf870529c43dc3d3509b439aaacc1b Mon Sep 17 00:00:00 2001 From: Alberto Molina Date: Fri, 10 Oct 2025 09:48:30 +0200 Subject: [PATCH 2/4] fix: test name changed Signed-off-by: Alberto Molina --- contracts/test/thread1/roleManagement.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test/thread1/roleManagement.test.ts b/contracts/test/thread1/roleManagement.test.ts index 06d32c046..304e4dd61 100644 --- a/contracts/test/thread1/roleManagement.test.ts +++ b/contracts/test/thread1/roleManagement.test.ts @@ -329,7 +329,7 @@ describe('➡️ Role Management Tests', function () { ).to.be.revertedWithCustomError(roleManagementFacet, 'AmountIsZero') }) - it('Granting CashInRole with 0 amount fails', async function () { + it('Granting CashInRole with max uint256 amount grants unlimited rights', async function () { await roleManagementFacet.grantRoles( [ROLES.cashin.hash], randomAccountList, From 2ae124e2c3c7b1e66895373cb29e70b31f624318 Mon Sep 17 00:00:00 2001 From: Alberto Molina Date: Fri, 10 Oct 2025 11:57:00 +0200 Subject: [PATCH 3/4] fix: test bug fixed Signed-off-by: Alberto Molina --- contracts/test/thread1/roleManagement.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/test/thread1/roleManagement.test.ts b/contracts/test/thread1/roleManagement.test.ts index 304e4dd61..71905104e 100644 --- a/contracts/test/thread1/roleManagement.test.ts +++ b/contracts/test/thread1/roleManagement.test.ts @@ -311,7 +311,12 @@ describe('➡️ Role Management Tests', function () { }) it('Granting role to account 0 fails', async function () { - const listOfAccounts = randomAccountList + const listOfAccounts: string[] = [] + + for (let i = 0; i < randomAccountList.length; i++) { + listOfAccounts.push(randomAccountList[i]) + } + listOfAccounts.push(ADDRESS_ZERO) await expect( From 73b109203db27616392ccbe08c16a3ceb4144804 Mon Sep 17 00:00:00 2001 From: Alberto Molina Date: Fri, 10 Oct 2025 12:41:41 +0200 Subject: [PATCH 4/4] feat: sdk turns 0 amount to uint max Signed-off-by: Alberto Molina --- .../grantMultiRoles/GrantMultiRolesCommandHandler.ts | 11 ++++++++--- sdk/src/core/Constants.ts | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts b/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts index b0713c376..96702fc71 100644 --- a/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts +++ b/sdk/src/app/usecase/command/stablecoin/roles/grantMultiRoles/GrantMultiRolesCommandHandler.ts @@ -19,6 +19,7 @@ */ import { ICommandHandler } from '../../../../../../core/command/CommandHandler.js'; +import { UINT256_MAX } from '../../../../../../core/Constants.js'; import { CommandHandler } from '../../../../../../core/decorator/CommandHandlerDecorator.js'; import { lazyInject } from '../../../../../../core/decorator/LazyInjectDecorator.js'; import BigDecimal from '../../../../../../domain/context/shared/BigDecimal.js'; @@ -72,9 +73,13 @@ export class GrantMultiRolesCommandHandler const amountsFormatted: BigDecimal[] = []; amounts.forEach((amount) => { - amountsFormatted.push( - BigDecimal.fromString(amount, capabilities.coin.decimals), - ); + if (amount == '0') { + amountsFormatted.push(BigDecimal.fromString(UINT256_MAX)); + } else { + amountsFormatted.push( + BigDecimal.fromString(amount, capabilities.coin.decimals), + ); + } }); const res = await handler.grantRoles( diff --git a/sdk/src/core/Constants.ts b/sdk/src/core/Constants.ts index f59651a35..e1ab76941 100644 --- a/sdk/src/core/Constants.ts +++ b/sdk/src/core/Constants.ts @@ -78,3 +78,6 @@ export const CONFIG_RESERVE = export const DEFAULT_VERSION = 1; export const ONE_THOUSAND = 1000; + +export const UINT256_MAX = + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';