Skip to content

Commit

Permalink
fix: returning non-success response code instead of reverting when su…
Browse files Browse the repository at this point in the history
…pplykey is missing (#167) (#223)

Signed-off-by: Mariusz Jasuwienas <[email protected]>
  • Loading branch information
arianejasuwienas authored Feb 13, 2025
1 parent 0fe9477 commit b737b00
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 8 deletions.
1 change: 1 addition & 0 deletions contracts/HederaResponseCodes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
17 changes: 16 additions & 1 deletion contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IERC721} from "./IERC721.sol";
import {IHRC719} from "./IHRC719.sol";
import {IHederaTokenService} from "./IHederaTokenService.sol";
import {HederaResponseCodes} from "./HederaResponseCodes.sol";
import {KeyLib} from "./KeyLib.sol";

address constant HTS_ADDRESS = address(0x167);

Expand Down Expand Up @@ -56,11 +57,22 @@ contract HtsSystemContract is IHederaTokenService {
int64 responseCode,
int64 newTotalSupply,
int64[] memory serialNumbers
) {
return _mintToken(token, amount, true);
}

function _mintToken(address token, int64 amount, bool checkSupplyKey) private returns (
int64 responseCode,
int64 newTotalSupply,
int64[] memory serialNumbers
) {
require(token != address(0), "mintToken: invalid token");
require(amount > 0, "mintToken: invalid amount");

(int64 tokenInfoResponseCode, TokenInfo memory tokenInfo) = IHederaTokenService(token).getTokenInfo(token);
if (checkSupplyKey && !KeyLib.keyExists(0x10, tokenInfo)) { // 0x10 - supply key
return (HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY, tokenInfo.totalSupply, new int64[](0));
}
require(tokenInfoResponseCode == HederaResponseCodes.SUCCESS, "mintToken: failed to get token info");

address treasuryAccount = tokenInfo.token.treasury;
Expand All @@ -82,6 +94,9 @@ contract HtsSystemContract is IHederaTokenService {
require(amount > 0, "burnToken: invalid amount");

(int64 tokenInfoResponseCode, TokenInfo memory tokenInfo) = IHederaTokenService(token).getTokenInfo(token);
if (!KeyLib.keyExists(0x10, tokenInfo)) {
return (HederaResponseCodes.TOKEN_HAS_NO_SUPPLY_KEY, tokenInfo.totalSupply);
}
require(tokenInfoResponseCode == HederaResponseCodes.SUCCESS, "burnToken: failed to get token info");

address treasuryAccount = tokenInfo.token.treasury;
Expand Down Expand Up @@ -188,7 +203,7 @@ contract HtsSystemContract is IHederaTokenService {
HtsSystemContract(tokenAddress).__setTokenInfo(tokenType_, tokenInfo, decimals_);

if (initialTotalSupply > 0) {
this.mintToken(tokenAddress, initialTotalSupply, new bytes[](0));
_mintToken(tokenAddress, initialTotalSupply, false);
}

responseCode = HederaResponseCodes.SUCCESS;
Expand Down
16 changes: 16 additions & 0 deletions contracts/KeyLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {IHederaTokenService} from "./IHederaTokenService.sol";

library KeyLib {
function keyExists(uint keyType, IHederaTokenService.TokenInfo memory tokenInfo) internal pure returns (bool) {
for (uint256 i = 0; i < tokenInfo.token.tokenKeys.length; i++) {
if (tokenInfo.token.tokenKeys[i].keyType == keyType) {
IHederaTokenService.KeyValue memory key = tokenInfo.token.tokenKeys[i].key;
return key.contractId != address(0) || (key.ECDSA_secp256k1.length + key.ed25519.length) > 0;
}
}
return false;
}
}
70 changes: 65 additions & 5 deletions test/HTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ contract HTSTest is Test, TestSetup {
bytes[] memory metadata = new bytes[](0);
int64 initialTotalSupply = 10000000005000000;
IHederaTokenService.TokenInfo memory tokenInfo;
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
keys[0].keyType = 0x10; // Supply key
keys[0].key.ECDSA_secp256k1 = hex"0242b7c3beea2af6dfcc874c41d1332463407e283f602ce8ef2cbe324823561b6f";
tokenInfo.token = IHederaTokenService.HederaToken(
"USD Coin",
"USDC",
Expand All @@ -226,7 +229,7 @@ contract HTSTest is Test, TestSetup {
false,
initialTotalSupply + amount,
false,
new IHederaTokenService.TokenKey[](0),
keys,
IHederaTokenService.Expiry(0, address(0), 0)
);

Expand All @@ -239,6 +242,33 @@ contract HTSTest is Test, TestSetup {
IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, metadata);
}

function test_mintToken_should_fail_with_no_supplyKey() external {
address token = USDC;
int64 amount = 1000;
bytes[] memory metadata = new bytes[](0);
int64 initialTotalSupply = 10000000005000000;
IHederaTokenService.TokenInfo memory tokenInfo;
tokenInfo.token = IHederaTokenService.HederaToken(
"USD Coin",
"USDC",
address(0),
"USDC HBAR",
false,
initialTotalSupply + amount,
false,
new IHederaTokenService.TokenKey[](0),
IHederaTokenService.Expiry(0, address(0), 0)
);

vm.mockCall(
token,
abi.encode(IHederaTokenService.getTokenInfo.selector),
abi.encode(HederaResponseCodes.SUCCESS, tokenInfo)
);
(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 {
address token = address(0);
int64 amount = 1000;
Expand All @@ -258,10 +288,10 @@ 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;
int64 amount = 1000;
int64 initialTotalSupply = 5000;
int64 initialTotalSupply = int64(int256(IERC20(token).totalSupply()));
uint256 initialTreasuryBalance = IERC20(token).balanceOf(treasury);

(int64 responseCodeMint, int64 newTotalSupplyAfterMint, int64[] memory serialNumbers) = IHederaTokenService(HTS_ADDRESS).mintToken(token, amount, new bytes[](0));
Expand All @@ -287,6 +317,9 @@ contract HTSTest is Test, TestSetup {
int64 initialTotalSupply = 5000;
int64[] memory serialNumbers = new int64[](0);
IHederaTokenService.TokenInfo memory tokenInfo;
IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1);
keys[0].keyType = 0x10; // Supply key
keys[0].key.ECDSA_secp256k1 = hex"0242b7c3beea2af6dfcc874c41d1332463407e283f602ce8ef2cbe324823561b6f";
tokenInfo.token = IHederaTokenService.HederaToken(
"My Crypto Token is the name which the string length is greater than 31",
"Token symbol must be exactly 32!",
Expand All @@ -295,7 +328,7 @@ contract HTSTest is Test, TestSetup {
false,
initialTotalSupply + amount,
false,
new IHederaTokenService.TokenKey[](0),
keys,
IHederaTokenService.Expiry(0, address(0), 0)
);

Expand All @@ -308,6 +341,33 @@ contract HTSTest is Test, TestSetup {
IHederaTokenService(HTS_ADDRESS).burnToken(token, amount, serialNumbers);
}

function test_burnToken_should_revert_with_no_supplier() external {
address token = MFCT;
int64 amount = 1000;
int64 initialTotalSupply = 5000;
int64[] memory serialNumbers = new int64[](0);
IHederaTokenService.TokenInfo memory tokenInfo;
tokenInfo.token = IHederaTokenService.HederaToken(
"My Crypto Token is the name which the string length is greater than 31",
"Token symbol must be exactly 32!",
address(0),
"",
false,
initialTotalSupply + amount,
false,
new IHederaTokenService.TokenKey[](0),
IHederaTokenService.Expiry(0, address(0), 0)
);

vm.mockCall(
token,
abi.encode(IHederaTokenService.getTokenInfo.selector),
abi.encode(HederaResponseCodes.SUCCESS, tokenInfo)
);
(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 {
address token = address(0);
int64 amount = 1000;
Expand Down
3 changes: 1 addition & 2 deletions test/hts.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ describe('::e2e', function () {
title: 'with Fungible Token with no supply key',
create: () => createToken({ noSupplyKey: true }),
tests: () => {
// https://github.com/hashgraph/hedera-forking/issues/167
it.skip('should not mint because there is no supply key', async function () {
it('should not mint because there is no supply key', async function () {
const preTreasuryBalance = await ERC20['balanceOf'](ft.treasury.evmAddress);
const preTotalSupply = await ERC20['totalSupply']();

Expand Down

0 comments on commit b737b00

Please sign in to comment.