Skip to content

Commit

Permalink
feat: approval support (#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 30e486a commit 15bf8a5
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 60 deletions.
68 changes: 65 additions & 3 deletions contracts/HtsSystemContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract HtsSystemContract is IHederaTokenService, IERC20Events, IERC721Events {

function cryptoTransfer(TransferList memory transferList, TokenTransferList[] memory tokenTransfers)
htsCall external returns (int64 responseCode) {
_cryptoHbarTransfer(transferList);
_cryptoHbarTransfers(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 @@ -927,7 +927,69 @@ contract HtsSystemContract is IHederaTokenService, IERC20Events, IERC721Events {
emit ApprovalForAll(sender, operator, approved);
}

function _cryptoHbarTransfer(TransferList memory transferList) internal virtual {
require(transferList.transfers.length == 0, "cryptoTransfer: hbar transfer is not supported");
function _cryptoHbarTransfers(TransferList memory transferList) internal {
int64 hbarBalance = 0;
for (uint256 i = 0; i < transferList.transfers.length; i++) {
// HBAR approval
if (transferList.transfers[i].isApproval) {
require(transferList.transfers[i].amount > 0, "cryptoTransfer: invalid amount");
hbarBalance += transferList.transfers[i].amount;
_approve(
msg.sender,
transferList.transfers[i].accountID,
uint256(uint64(-transferList.transfers[i].amount))
);
continue;
}
require(transferList.transfers[i].amount != 0, "cryptoTransfer: invalid amount");

// HBAR transfer from
if (transferList.transfers[i].amount < 0) {
uint256 fromAmount = uint256(uint64(-transferList.transfers[i].amount));
require(
fromAmount <= transferList.transfers[i].accountID.balance,
"cryptoTransfer: insufficient balance"
);

// HBAR transferring as owner
if (transferList.transfers[i].accountID == msg.sender) {
_updateHbarBalanceOnAccount(
transferList.transfers[i].accountID,
transferList.transfers[i].accountID.balance - fromAmount
);
continue;
}
// HBAR transferring from someone else's account
uint256 allowance = __allowance(transferList.transfers[i].accountID, msg.sender);
require(
allowance >= transferList.transfers[i].accountID.balance,
"cryptoTransfer: insufficient allowance"
);
_updateHbarBalanceOnAccount(
transferList.transfers[i].accountID,
transferList.transfers[i].accountID.balance - fromAmount
);
unchecked {
_approve(
transferList.transfers[i].accountID,
msg.sender,
allowance - transferList.transfers[i].accountID.balance
);
}
continue;
}

// HBAR transfer to
uint256 toAmount = uint256(uint64(transferList.transfers[i].amount));
_updateHbarBalanceOnAccount(
transferList.transfers[i].accountID,
transferList.transfers[i].accountID.balance + toAmount
);
}
require(hbarBalance == 0, "cryptoTransfer: unmatched hbar transfers ");
}

function _updateHbarBalanceOnAccount(address account, uint256 newBalance) internal virtual {
revert("cryptoTransfer: hbar transfer on non-payable method is not supported");
}
}
31 changes: 2 additions & 29 deletions contracts/HtsSystemContractJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -474,34 +474,7 @@ contract HtsSystemContractJson is HtsSystemContract {
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 ");
function _updateHbarBalanceOnAccount(address account, uint256 newBalance) internal override {
vm.deal(account, newBalance);
}
}
29 changes: 1 addition & 28 deletions test/HTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ 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, approved transfers are not supported");
vm.expectRevert("cryptoTransfer: insufficient allowance");
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
Expand Down Expand Up @@ -980,31 +980,4 @@ contract HTSTest is Test, TestSetup {
new IHederaTokenService.TokenTransferList[](0)
);
}

function test_HTS_cryptoTransfer_test_reject_hbar_approval() 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)),
true
);
IHederaTokenService.TransferList memory transferList;
transferList.transfers = new IHederaTokenService.AccountAmount[](2);
transferList.transfers[0] = transfer1;
transferList.transfers[1] = transfer2;
vm.deal(owner, hbarToRecipient);
vm.expectRevert("cryptoTransfer: hbar approval is not supported");
vm.prank(owner);
IHederaTokenService(HTS_ADDRESS).cryptoTransfer(
transferList,
new IHederaTokenService.TokenTransferList[](0)
);
}
}

0 comments on commit 15bf8a5

Please sign in to comment.