From 1e88199042a80028993b120010dd7033bc28289e Mon Sep 17 00:00:00 2001 From: Kristen Pire Date: Tue, 12 Nov 2024 12:32:37 +0100 Subject: [PATCH 1/3] feat(Token): adding setMintAllowanceFroAdmin function, it enalbe the system key to set mint allowance using a pre-signed messaage from an admin --- src/Token.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Token.sol b/src/Token.sol index 9edb38e..c8ef6ca 100644 --- a/src/Token.sol +++ b/src/Token.sol @@ -148,6 +148,28 @@ contract Token is _setMintAllowance(account, amount); } + function setMintAllowanceForAdmin( + address from, + address account, + uint256 amount, + bytes memory signature + ) public onlySystemAccounts { + require( + from.isValidSignatureNow( + 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, //change this hash to chosen static message hash + signature + ), + "signature/hash does not match" + ); + + require( + isAdminAccount(from), + "Token: from is not a admin account" + ); + + _setMintAllowance(account, amount); + } + // EIP-2612 helper function getPermitDigest( address owner, From 475341e5b93415fc58d4308183088ff3c502aee8 Mon Sep 17 00:00:00 2001 From: Kristen Pire Date: Tue, 12 Nov 2024 12:33:43 +0100 Subject: [PATCH 2/3] feat(token): adding 100% coverage tests for setMintAllowanceForAdmin --- test/Mintable.t.sol | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/Mintable.t.sol b/test/Mintable.t.sol index e2a9052..f48c96a 100644 --- a/test/Mintable.t.sol +++ b/test/Mintable.t.sol @@ -99,6 +99,89 @@ contract MintableTokenTest is Test { assertEq(token.balanceOf(user), 100); } + + function test_system_account_can_set_mint_allowance_for_an_admin() + public + { + test_owner_can_set_max_mint_allowance(); + address user = vm.addr(userPrivateKey); + + token.addAdminAccount(user); + token.addSystemAccount(system); + + bytes32 hash = 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f; + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + + vm.startPrank(system); + token.setMintAllowanceForAdmin(user, system, 1000, signature); + vm.stopPrank(); + + assertEq(token.getMintAllowance(system), 1000); + + } + + function test_system_account_cannot_set_mint_allowance_for_an_admin_with_wrong_signature() + public + { + test_owner_can_set_max_mint_allowance(); + address user = vm.addr(userPrivateKey); + + token.addAdminAccount(user); + token.addSystemAccount(system); + + bytes32 hash = 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f; + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); + r = bytes32(uint256(r) ^ 1); // Flip one bit in `r` to invalidate the signature + bytes memory signature = abi.encodePacked(r, s, v); + + + vm.startPrank(system); + vm.expectRevert("signature/hash does not match"); + token.setMintAllowanceForAdmin(user, system, 1000, signature); + vm.stopPrank(); + } + + function test_system_account_cannot_set_mint_allowance_for_an_admin_with_wrong_messae() + public + { + test_owner_can_set_max_mint_allowance(); + address user = vm.addr(userPrivateKey); + + token.addAdminAccount(user); + token.addSystemAccount(system); + + bytes32 hash = 0x0000000000000000000000000000000000000000000000000000000000001234; + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); + r = bytes32(uint256(r) ^ 1); // Flip one bit in `r` to invalidate the signature + bytes memory signature = abi.encodePacked(r, s, v); + + + vm.startPrank(system); + vm.expectRevert("signature/hash does not match"); + token.setMintAllowanceForAdmin(user, system, 1000, signature); + vm.stopPrank(); + } + + function test_system_account_cannot_set_mint_allowance_for_a_non_admin() + public + { + test_owner_can_set_max_mint_allowance(); + address user = vm.addr(userPrivateKey); + + // Not adding the user as admin + token.addSystemAccount(system); + + bytes32 hash = 0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f; + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, hash); + bytes memory signature = abi.encodePacked(r, s, v); + + vm.startPrank(system); + vm.expectRevert("Token: from is not a admin account"); + token.setMintAllowanceForAdmin(user, system, 1000, signature); + vm.stopPrank(); + } + function test_system_account_cannot_mint_tokens_above_mint_allowance() public { From 08cba4dfe43de42fc16127ab04f1e88bbc3becca Mon Sep 17 00:00:00 2001 From: Kristen Pire Date: Tue, 12 Nov 2024 12:45:45 +0100 Subject: [PATCH 3/3] chore(token): add comment about setMintAllowanceForAdmin --- src/Token.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Token.sol b/src/Token.sol index c8ef6ca..29cb620 100644 --- a/src/Token.sol +++ b/src/Token.sol @@ -148,6 +148,24 @@ contract Token is _setMintAllowance(account, amount); } + /** + * @notice Allows a system account to set the mint allowance for an admin account, using an admin's signature for authorization. + * + * The message expected to be signed by the admin is currently hardcoded with a static hash + * (`0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f`). This choice could be updated in future + * implementations to allow for dynamic messages, depending on design requirements. + * + * Requirements: + * - `from` must provide a valid signature matching the hardcoded hash to confirm authorization. + * - `from` must be a recognized admin account. + * - The function can only be called by accounts designated as system accounts. + * + * @param from The address of the admin providing authorization for setting the mint allowance. + * @param account The address of the account for which the mint allowance is to be set. + * @param amount The new mint allowance to be assigned to the specified account. + * @param signature The signature of the admin (`from`), authorizing this allowance adjustment. + */ + function setMintAllowanceForAdmin( address from, address account,