From 488c8e1985df34270ee4f3140c2ae4841879787d Mon Sep 17 00:00:00 2001 From: Mariusz Jasuwienas Date: Thu, 6 Feb 2025 15:12:03 +0100 Subject: [PATCH] feat: validating supplier key in mint and burn (#167) Signed-off-by: Mariusz Jasuwienas --- contracts/HederaResponseCodes.sol | 1 + contracts/HtsSystemContract.sol | 23 +++++++++++++++++++++-- test/HTS.t.sol | 22 ++++++++++++---------- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/contracts/HederaResponseCodes.sol b/contracts/HederaResponseCodes.sol index 53b8e4ac..a46b065e 100644 --- a/contracts/HederaResponseCodes.sol +++ b/contracts/HederaResponseCodes.sol @@ -3,4 +3,5 @@ pragma solidity ^0.8.0; library HederaResponseCodes { int32 internal constant SUCCESS = 22; // The transaction succeeded + int32 internal constant TOKEN_HAS_NO_SUPPLY_KEY = 180; } diff --git a/contracts/HtsSystemContract.sol b/contracts/HtsSystemContract.sol index 98e1df06..57ad91b1 100644 --- a/contracts/HtsSystemContract.sol +++ b/contracts/HtsSystemContract.sol @@ -56,7 +56,15 @@ contract HtsSystemContract is IHederaTokenService { assembly { accountId := sload(slot) } } - function mintToken(address token, int64 amount, bytes[] memory) htsCall external returns ( + function mintToken(address token, int64 amount, bytes[] memory data) htsCall external returns ( + int64 responseCode, + int64 newTotalSupply, + int64[] memory serialNumbers + ) { + return mintToken(token, amount, false); + } + + function mintToken(address token, int64 amount, bool ignoreSupplyKeyCheck) htsCall internal returns ( int64 responseCode, int64 newTotalSupply, int64[] memory serialNumbers @@ -67,6 +75,13 @@ contract HtsSystemContract is IHederaTokenService { (int64 tokenInfoResponseCode, TokenInfo memory tokenInfo) = IHederaTokenService(token).getTokenInfo(token); require(tokenInfoResponseCode == HederaResponseCodes.SUCCESS, "mintToken: failed to get token info"); + if (!ignoreSupplyKeyCheck) { + address supplyAccount = ITokenKey(token).getKeyAddress(0x10); // 0x10 - supply key + if (supplyAccount == address(0) || msg.sender != supplyAccount) { + return (HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY, tokenInfo.totalSupply, new int64[](0)); + } + } + address treasuryAccount = tokenInfo.token.treasury; require(treasuryAccount != address(0), "mintToken: invalid account"); @@ -88,6 +103,10 @@ contract HtsSystemContract is IHederaTokenService { (int64 tokenInfoResponseCode, TokenInfo memory tokenInfo) = IHederaTokenService(token).getTokenInfo(token); require(tokenInfoResponseCode == HederaResponseCodes.SUCCESS, "burnToken: failed to get token info"); + address supplyAccount = ITokenKey(token).getKeyAddress(0x10); // 0x10 - supply key + if (supplyAccount == address(0) || msg.sender != supplyAccount) { + return (HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY, tokenInfo.totalSupply); + } address treasuryAccount = tokenInfo.token.treasury; require(treasuryAccount != address(0), "burnToken: invalid account"); @@ -189,7 +208,7 @@ contract HtsSystemContract is IHederaTokenService { deployHIP719Proxy(tokenAddress); if (initialTotalSupply > 0) { - this.mintToken(tokenAddress, initialTotalSupply, new bytes[](0)); + mintToken(tokenAddress, initialTotalSupply, true); } responseCode = HederaResponseCodes.SUCCESS; diff --git a/test/HTS.t.sol b/test/HTS.t.sol index 6ad0b25d..cb251d82 100644 --- a/test/HTS.t.sol +++ b/test/HTS.t.sol @@ -198,7 +198,8 @@ contract HTSTest is Test, TestSetup { int64 initialTotalSupply = 10000000005000000; uint256 initialTreasuryBalance = IERC20(token).balanceOf(treasury); bytes[] memory metadata = new bytes[](0); - + address supplier = 0x00000000000000000000000000000000001c1aEC; + vm.prank(supplier); (int64 responseCode, int64 newTotalSupply, int64[] memory serialNumbers) = IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, metadata); assertEq(responseCode, HederaResponseCodes.SUCCESS); assertEq(serialNumbers.length, 0); @@ -235,8 +236,8 @@ contract HTSTest is Test, TestSetup { abi.encode(IHederaTokenService.getTokenInfo.selector), abi.encode(HederaResponseCodes.SUCCESS, tokenInfo) ); - vm.expectRevert(bytes("mintToken: invalid account")); - IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, metadata); + (int64 code, , ) = IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, metadata); + assertEq(code, HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY); } function test_mintToken_should_revert_with_invalid_token() external { @@ -258,12 +259,13 @@ contract HTSTest is Test, TestSetup { } function test_burnToken_should_succeed_with_valid_input() external { - address token = MFCT; - address treasury = MFCT_TREASURY; + address token = USDC; + address treasury = USDC_TREASURY; + address supplier = 0x00000000000000000000000000000000001c1aEC; int64 amount = 1000; - int64 initialTotalSupply = 5000; + int64 initialTotalSupply = int64(int256(IERC20(token).totalSupply())); uint256 initialTreasuryBalance = IERC20(token).balanceOf(treasury); - + vm.startPrank(supplier); (int64 responseCodeMint, int64 newTotalSupplyAfterMint, int64[] memory serialNumbers) = IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, new bytes[](0)); assertEq(responseCodeMint, HederaResponseCodes.SUCCESS); assertEq(serialNumbers.length, 0); @@ -274,7 +276,7 @@ contract HTSTest is Test, TestSetup { assertEq(responseCodeBurn, HederaResponseCodes.SUCCESS); assertEq(newTotalSupplyAfterBurn, initialTotalSupply); assertEq(IERC20(token).balanceOf(treasury), uint64(initialTreasuryBalance)); - + vm.stopPrank(); (int64 responseCodeGet, IHederaTokenService.TokenInfo memory tokenInfo) = IHederaTokenService(HTS_ADDRESS).getTokenInfo(token); assertEq(responseCodeGet, HederaResponseCodes.SUCCESS); @@ -304,8 +306,8 @@ contract HTSTest is Test, TestSetup { abi.encode(IHederaTokenService.getTokenInfo.selector), abi.encode(HederaResponseCodes.SUCCESS, tokenInfo) ); - vm.expectRevert(bytes("burnToken: invalid account")); - IHederaTokenService(HTS_ADDRESS).burnToken(token, amount, serialNumbers); + (int64 code, ) = IHederaTokenService(HTS_ADDRESS).burnToken(token, amount, serialNumbers); + assertEq(code, HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY); } function test_burnToken_should_revert_with_invalid_token() external {