Skip to content

Commit

Permalink
feat: use nonpayable method for hbar transfers as suggested (#195)
Browse files Browse the repository at this point in the history
Signed-off-by: Mariusz Jasuwienas <[email protected]>
  • Loading branch information
arianejasuwienas committed Jan 23, 2025
1 parent 33a75f4 commit 30e486a
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 58 deletions.
27 changes: 6 additions & 21 deletions contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,8 @@ contract HtsSystemContract is IHederaTokenService, IERC20Events, IERC721Events {
}

function cryptoTransfer(TransferList memory transferList, TokenTransferList[] memory tokenTransfers)
payable htsCall external returns (int64 responseCode) {
uint256 hbarsReceived = msg.value;
int64 hbarBalance = 0;
for (uint256 hbarIndex = 0; hbarIndex < transferList.transfers.length; hbarIndex++) {
require(!transferList.transfers[hbarIndex].isApproval, "cryptoTransfer: hbar approval is not supported");
hbarBalance += transferList.transfers[hbarIndex].amount;
if (transferList.transfers[hbarIndex].amount < 0) {
require(
transferList.transfers[hbarIndex].accountID == msg.sender,
"cryptoTransfer: hbar transfer allowed only from the msg sender account"
);
continue;
}
require(transferList.transfers[hbarIndex].amount > 0, "cryptoTransfer: invalid amount");
uint256 value = uint256(uint64(transferList.transfers[hbarIndex].amount));
require(hbarsReceived >= value, "cryptoTransfer: insufficient balance");
hbarsReceived -= value;
(bool success, ) = transferList.transfers[hbarIndex].accountID.call{value: value}("");
require(success, "cryptoTransfer: hbar transfer failure");
}
require(hbarBalance == 0 && hbarsReceived == 0, "cryptoTransfer: unmatched hbar transfers ");
htsCall external returns (int64 responseCode) {
_cryptoHbarTransfer(transferList);
for (uint256 tokenIndex = 0; tokenIndex < tokenTransfers.length; tokenIndex++) {
require(tokenTransfers[tokenIndex].token != address(0), "cryptoTransfer: invalid token");
uint256 validFungibleTransfersCount = 0;
Expand Down Expand Up @@ -945,4 +926,8 @@ contract HtsSystemContract is IHederaTokenService, IERC20Events, IERC721Events {
assembly { sstore(slot, approved) }
emit ApprovalForAll(sender, operator, approved);
}

function _cryptoHbarTransfer(TransferList memory transferList) internal virtual {
require(transferList.transfers.length == 0, "cryptoTransfer: hbar transfer is not supported");
}
}
31 changes: 31 additions & 0 deletions contracts/HtsSystemContractJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -473,4 +473,35 @@ contract HtsSystemContractJson is HtsSystemContract {
function _scratchAddr() private view returns (address) {
return address(bytes20(keccak256(abi.encode(address(this)))));
}

function _cryptoHbarTransfer(TransferList memory transferList) internal override {
int64 hbarBalance = 0;
for (uint256 hbarIndex = 0; hbarIndex < transferList.transfers.length; hbarIndex++) {
require(transferList.transfers[hbarIndex].amount != 0, "cryptoTransfer: invalid amount");
require(!transferList.transfers[hbarIndex].isApproval, "cryptoTransfer: hbar approval is not supported");
hbarBalance += transferList.transfers[hbarIndex].amount;
if (transferList.transfers[hbarIndex].amount < 0) {
require(
transferList.transfers[hbarIndex].accountID == msg.sender,
"cryptoTransfer: hbar transfer allowed only from the msg sender account, approved transfers are not supported"
);
uint256 fromAmount = uint256(uint64(-transferList.transfers[hbarIndex].amount));
require(
fromAmount <= transferList.transfers[hbarIndex].accountID.balance,
"cryptoTransfer: insufficient balance"
);
vm.deal(
transferList.transfers[hbarIndex].accountID,
transferList.transfers[hbarIndex].accountID.balance - fromAmount
);
continue;
}
uint256 toAmount = uint256(uint64(transferList.transfers[hbarIndex].amount));
vm.deal(
transferList.transfers[hbarIndex].accountID,
transferList.transfers[hbarIndex].accountID.balance + toAmount
);
}
require(hbarBalance == 0, "cryptoTransfer: unmatched hbar transfers ");
}
}
2 changes: 1 addition & 1 deletion contracts/IHederaTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ interface IHederaTokenService {
/// @param tokenTransfers the list of token transfers to do
/// @custom:version 0.3.0 the signature of the previous version was cryptoTransfer(TokenTransferList[] memory tokenTransfers)
function cryptoTransfer(TransferList memory transferList, TokenTransferList[] memory tokenTransfers)
external payable
external
returns (int64 responseCode);

/// Mints an amount of the token to the defined treasury account
Expand Down
43 changes: 7 additions & 36 deletions test/HTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -915,9 +915,7 @@ contract HTSTest is Test, TestSetup {
assertEq(initialRecipient1Balance, 0);
assertEq(initialRecipient2Balance, 0);
vm.prank(owner);
vm.expectCall(recipient1, hbarToRecipient1, "");
vm.expectCall(recipient2, hbarToRecipient2, "");
int64 code = IHederaTokenService(HTS_ADDRESS).cryptoTransfer{value: hbarToRecipient1 + hbarToRecipient2}(
int64 code = IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
Expand Down Expand Up @@ -948,18 +946,19 @@ contract HTSTest is Test, TestSetup {
transferList.transfers[1] = transfer2;
vm.deal(owner, hbarToRecipient);
vm.deal(sender, hbarToRecipient);
vm.expectRevert("cryptoTransfer: hbar transfer allowed only from the msg sender account");
vm.expectRevert("cryptoTransfer: hbar transfer allowed only from the msg sender account, approved transfers are not supported");
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer{value: hbarToRecipient}(
IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
}

function test_HTS_cryptoTransfer_test_reject_insufficient_value_send() external {
function test_HTS_cryptoTransfer_test_reject_insufficient_balance() external {
address owner = 0x4D1c823b5f15bE83FDf5adAF137c2a9e0E78fE15;
address recipient = makeAddr("recipient");
uint256 hbarToRecipient = 1 ether;
vm.deal(owner, 0.5 ether); // Not enough
IHederaTokenService.AccountAmount memory transfer1 = IHederaTokenService.AccountAmount(
owner,
-int64(uint64(hbarToRecipient)),
Expand All @@ -974,10 +973,9 @@ contract HTSTest is Test, TestSetup {
transferList.transfers = new IHederaTokenService.AccountAmount[](2);
transferList.transfers[0] = transfer1;
transferList.transfers[1] = transfer2;
vm.deal(owner, hbarToRecipient);
vm.expectRevert("cryptoTransfer: insufficient balance");
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer{value: hbarToRecipient - 0.5 ether}(
IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
Expand All @@ -1004,34 +1002,7 @@ contract HTSTest is Test, TestSetup {
vm.deal(owner, hbarToRecipient);
vm.expectRevert("cryptoTransfer: hbar approval is not supported");
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer{value: hbarToRecipient}(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
}

function test_HTS_cryptoTransfer_test_reject_wrong_value() external {
address owner = 0x4D1c823b5f15bE83FDf5adAF137c2a9e0E78fE15;
address recipient = makeAddr("recipient");
uint256 hbarToRecipient = 1 ether;
IHederaTokenService.AccountAmount memory transfer1 = IHederaTokenService.AccountAmount(
owner,
-int64(uint64(hbarToRecipient)),
false
);
IHederaTokenService.AccountAmount memory transfer2 = IHederaTokenService.AccountAmount(
recipient,
int64(uint64(hbarToRecipient)),
false
);
IHederaTokenService.TransferList memory transferList;
transferList.transfers = new IHederaTokenService.AccountAmount[](2);
transferList.transfers[0] = transfer1;
transferList.transfers[1] = transfer2;
vm.deal(owner, hbarToRecipient);
vm.expectRevert();
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer{value: hbarToRecipient + 0.5 ether}(
IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
Expand Down

0 comments on commit 30e486a

Please sign in to comment.