Skip to content

Commit

Permalink
feat: freezing (#197)
Browse files Browse the repository at this point in the history
Signed-off-by: Mariusz Jasuwienas <[email protected]>
  • Loading branch information
arianejasuwienas committed Feb 10, 2025
1 parent 4156c82 commit ce23d50
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 3 deletions.
2 changes: 1 addition & 1 deletion contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ contract HtsSystemContract is IHederaTokenService {

function freezeToken(address token, address account) htsCall kyc(token) notPaused(token) external returns (int64 responseCode) {
(, TokenInfo memory info) = getTokenInfo(token);
require(_extractKeyAddress(0x4, info) == msg.sender, "grantTokenKyc: Only allowed for freezable tokens");
require(_extractKeyAddress(0x4, info) == msg.sender, "freezeToken: only allowed for freezable tokens");
responseCode = IHederaTokenService(token).freezeToken(token, account);
}

Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ interface IMirrorNodeClient {
token_id: string;
automatic_association: boolean;
kyc_status: 'NOT_APPLICABLE' | 'GRANTED' | 'REVOKED';
frozen_status: 'NOT_APPLICABLE' | 'FROZEN' | 'UNFROZEN';
}[];
} | null>;

Expand Down
17 changes: 17 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,23 @@ async function getHtsStorageAt(address, requestedSlot, blockNumber, mirrorNodeCl
);
}

// Encoded `address(tokenId).isFrozen(tokenId, accountId)` slot
// slot(256) = `isFrozen`selector(32) + padding(192) + accountId(32)
if (
nrequestedSlot >> 32n ===
0x46de0fb1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000n
) {
const accountId = `0.0.${parseInt(requestedSlot.slice(-8), 16)}`;
const { tokens } = (await mirrorNodeClient.getTokenRelationship(accountId, tokenId)) ?? {
tokens: [],
};
const isFrozen = tokens.length > 0 && tokens[0].frozen_status === 'FROZEN';
return ret(
`0x${toIntHex256(isFrozen ? 1 : 0)}`,
`Token ${tokenId} is ${isFrozen ? 'frozen' : 'not frozen'} for account ${accountId}`
);
}

let unresolvedValues = persistentStorage.load(tokenId, blockNumber, nrequestedSlot);
if (unresolvedValues === undefined) {
const token = await mirrorNodeClient.getTokenById(tokenId, blockNumber);
Expand Down
73 changes: 73 additions & 0 deletions test/Freeze.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
import {TestSetup} from "./lib/TestSetup.sol";
import {HTS_ADDRESS} from "../contracts/HtsSystemContract.sol";
import {IHederaTokenService} from "../contracts/IHederaTokenService.sol";
import {HederaResponseCodes} from "../contracts/HederaResponseCodes.sol";
import {IERC20} from "../contracts/IERC20.sol";
import {IERC721} from "../contracts/IERC721.sol";

contract FreezeTest is Test, TestSetup {

address private token;
address private owner;
address private to;
uint256 private amount = 4_000000;

function setUp() external {
setUpMockStorageForNonFork();

if (testMode == TestMode.JSON_RPC) vm.skip(true);

IHederaTokenService.HederaToken memory hederaToken;
hederaToken.name = "Token name";
hederaToken.symbol = "Token symbol";
hederaToken.treasury = makeAddr("Token treasury");
hederaToken.tokenKeys = new IHederaTokenService.TokenKey[](1);
owner = makeAddr("pause");

hederaToken.tokenKeys[0].key.contractId = owner;

hederaToken.tokenKeys[0].keyType = 0x4;
(, token) = IHederaTokenService(HTS_ADDRESS).createFungibleToken{value: 1000}(hederaToken, 1000000, 4);
vm.assertNotEq(token, address(0));
to = makeAddr("bob");
}

function test_HTS_transferToken_success_when_not_frozen() public {
address from = makeAddr("from");
deal(token, from, amount);
vm.prank(from);
IHederaTokenService(HTS_ADDRESS).transferToken(token, from, to, int64(int256(amount)));
assertEq(IERC20(token).balanceOf(to), amount);
}

function test_HTS_freezing_success_with_correct_key() public {
address from = makeAddr("bob");
vm.startPrank(owner);
int64 freezeCode = IHederaTokenService(HTS_ADDRESS).freezeToken(token, from);
assertEq(freezeCode, HederaResponseCodes.SUCCESS);
int64 unfreezeCode = IHederaTokenService(HTS_ADDRESS).unfreezeToken(token, from);
assertEq(unfreezeCode, HederaResponseCodes.SUCCESS);
vm.stopPrank();
}

function test_HTS_freezing_failure_with_incorrect_key() public {
vm.expectRevert("freezeToken: only allowed for freezable tokens");
vm.prank(makeAddr("bob"));
IHederaTokenService(HTS_ADDRESS).freezeToken(token, makeAddr("alice"));
}

function test_HTS_transferToken_failure_when_paused() public {
address from = makeAddr("bob");
deal(token, from, amount);
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).freezeToken(token, from);
vm.expectRevert("notFrozen: token frozen");
vm.prank(from);
IHederaTokenService(HTS_ADDRESS).transferToken(token, from, to, int64(int256(amount)));
}
}
4 changes: 2 additions & 2 deletions test/Pause.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ contract PauseTest is Test, TestSetup {
assertEq(IERC20(token).balanceOf(to), amount);
}

function test_HTS_transferToken_pausing_success_with_correct_key() public {
function test_HTS_pausing_success_with_correct_key() public {
vm.prank(owner);
int64 code = IHederaTokenService(HTS_ADDRESS).pauseToken(token);
assertEq(code, HederaResponseCodes.SUCCESS);
}

function test_HTS_transferToken_pausing_failure_with_incorrect_key() public {
function test_HTS_pausing_failure_with_incorrect_key() public {
vm.expectRevert("pauseToken: only allowed for pause key");
vm.prank(makeAddr("from"));
IHederaTokenService(HTS_ADDRESS).pauseToken(token);
Expand Down

0 comments on commit ce23d50

Please sign in to comment.