From 3b0be2d5884916a10c411b6df2d3d23e2c138b72 Mon Sep 17 00:00:00 2001 From: Simeon Nakov Date: Thu, 9 Jan 2025 11:14:28 +0200 Subject: [PATCH] feat: renamed constant, added comments to example contracts, Signed-off-by: Simeon Nakov --- .../examples/hrc-904/Airdrop.sol | 76 ++++++++++++++++++- .../examples/hrc-904/CancelAirdrop.sol | 28 +++++++ .../examples/hrc-904/ClaimAirdrop.sol | 28 +++++++ .../examples/hrc-904/HRC904Contract.sol | 57 ++++++++++++++ .../examples/hrc-904/TokenReject.sol | 14 ++++ test/constants.js | 4 +- .../hedera-token-service/utils.js | 2 +- 7 files changed, 205 insertions(+), 4 deletions(-) diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/Airdrop.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/Airdrop.sol index ce85cc4ce..4b1297e5f 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/Airdrop.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/Airdrop.sol @@ -6,7 +6,25 @@ import {HederaTokenService} from "../../HederaTokenService.sol"; import {IHederaTokenService} from "../../IHederaTokenService.sol"; pragma experimental ABIEncoderV2; +// @title Airdrop Contract +// @notice Facilitates token airdrops on the Hedera network for both fungible and non-fungible tokens +// @dev Implements HRC-904 standard for token airdrops +// +// Recipients will receive tokens in one of these ways: +// - Immediately if already associated with the token +// - Immediately with auto-association if they have available slots +// - As a pending airdrop requiring claim if they have "receiver signature required" +// - As a pending airdrop requiring claim if they have no available auto-association slots +// +// All transfer fees and auto-renewal rent costs are charged to the transaction submitter contract Airdrop is HederaTokenService { + // @notice Airdrops fungible tokens from a sender to a single receiver + // @dev Builds airdrop struct and submits airdropTokens system contract call + // @param token The token address to airdrop + // @param sender The address sending the tokens + // @param receiver The address receiving the tokens + // @param amount The amount of tokens to transfer + // @return responseCode The response code from the airdrop operation (22 = success) function tokenAirdrop(address token, address sender, address receiver, int64 amount) public payable returns (int64 responseCode) { IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](1); IHederaTokenService.TokenTransferList memory airdrop; @@ -21,6 +39,13 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Airdrops a non-fungible token from a sender to a single receiver + // @dev Builds NFT airdrop struct and submits airdropTokens system contract call + // @param token The NFT token address + // @param sender The address sending the NFT + // @param receiver The address receiving the NFT + // @param serial The serial number of the NFT to transfer + // @return responseCode The response code from the airdrop operation (22 = success) function nftAirdrop(address token, address sender, address receiver, int64 serial) public payable returns (int64 responseCode) { IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](1); IHederaTokenService.TokenTransferList memory airdrop; @@ -38,6 +63,13 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Airdrops multiple fungible tokens of the same amount from a sender to a single receiver + // @dev Builds multiple token transfer structs and submits a single airdropTokens system contract call + // @param tokens Array of token addresses to airdrop + // @param sender The address sending all tokens + // @param receiver The address receiving all tokens + // @param amount The amount of each token to transfer + // @return responseCode The response code from the airdrop operation (22 = success) function tokenNAmountAirdrops(address[] memory tokens, address sender, address receiver, int64 amount) public payable returns (int64 responseCode) { uint256 length = tokens.length; IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](length); @@ -55,6 +87,13 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Airdrops multiple NFTs from a sender to a single receiver + // @dev Builds multiple NFT transfer structs and submits a single airdropTokens system contract call + // @param nfts Array of NFT token addresses + // @param sender The address sending all NFTs + // @param receiver The address receiving all NFTs + // @param serials Array of serial numbers corresponding to each NFT + // @return responseCode The response code from the airdrop operation (22 = success) function nftNAmountAirdrops(address[] memory nfts, address sender, address receiver, int64[] memory serials) public returns (int64 responseCode) { uint256 length = nfts.length; IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](length); @@ -74,6 +113,13 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Distributes the same amount of a fungible token from one sender to multiple receivers + // @dev Optimizes gas by building a single transfer list with multiple receivers + // @param token The token address to distribute + // @param sender The address sending the tokens + // @param receivers Array of addresses to receive the tokens + // @param amount The amount each receiver should get + // @return responseCode The response code from the airdrop operation (22 = success) function tokenAirdropDistribute( address token, address sender, @@ -117,7 +163,12 @@ contract Airdrop is HederaTokenService { return responseCode; } - + // @notice Distributes sequential NFTs from one sender to multiple receivers + // @dev Assigns sequential serial numbers starting from 1 to each receiver + // @param token The NFT token address + // @param sender The address sending the NFTs + // @param receivers Array of addresses to receive the NFTs + // @return responseCode The response code from the airdrop operation (22 = success) function nftAirdropDistribute(address token, address sender, address[] memory receivers) public payable returns (int64 responseCode) { uint256 length = receivers.length; IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](1); @@ -139,6 +190,17 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Performs a mixed airdrop of both fungible and non-fungible tokens + // @dev Combines multiple token types into a single airdropTokens call for gas efficiency + // @param token Array of fungible token addresses + // @param nft Array of NFT token addresses + // @param tokenSenders Array of addresses sending the fungible tokens + // @param tokenReceivers Array of addresses receiving the fungible tokens + // @param nftSenders Array of addresses sending the NFTs + // @param nftReceivers Array of addresses receiving the NFTs + // @param tokenAmount Amount of each fungible token to transfer + // @param serials Array of serial numbers for the NFTs + // @return responseCode The response code from the airdrop operation (22 = success) function mixedAirdrop(address[] memory token, address[] memory nft, address[] memory tokenSenders, address[] memory tokenReceivers, address[] memory nftSenders, address[] memory nftReceivers, int64 tokenAmount, int64[] memory serials) public payable returns (int64 responseCode) { uint256 length = tokenSenders.length + nftSenders.length; IHederaTokenService.TokenTransferList[] memory tokenTransfers = new IHederaTokenService.TokenTransferList[](length); @@ -167,6 +229,12 @@ contract Airdrop is HederaTokenService { return responseCode; } + // @notice Internal helper to prepare AccountAmount array for token transfers + // @dev Creates a transfer pair with negative amount for sender and positive for receiver + // @param sender The address sending tokens + // @param receiver The address receiving tokens + // @param amount The amount to transfer + // @return transfers Array containing the sender and receiver transfer details function prepareAA(address sender, address receiver, int64 amount) internal pure returns (IHederaTokenService.AccountAmount[] memory transfers) { IHederaTokenService.AccountAmount memory aa1; aa1.accountID = sender; @@ -180,6 +248,12 @@ contract Airdrop is HederaTokenService { return transfers; } + // @notice Internal helper to prepare NFT transfer struct + // @dev Sets up sender, receiver and serial number for an NFT transfer + // @param sender The address sending the NFT + // @param receiver The address receiving the NFT + // @param serial The serial number of the NFT + // @return nftTransfer The prepared NFT transfer struct function prepareNftTransfer(address sender, address receiver, int64 serial) internal pure returns (IHederaTokenService.NftTransfer memory nftTransfer) { nftTransfer.senderAccountID = sender; nftTransfer.receiverAccountID = receiver; diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol index 58df63264..6195638f5 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/CancelAirdrop.sol @@ -6,8 +6,22 @@ import {HederaTokenService} from "../../HederaTokenService.sol"; import {IHederaTokenService} from "../../IHederaTokenService.sol"; pragma experimental ABIEncoderV2; +// @title Cancel Airdrop Contract +// @notice Facilitates cancellation of pending token airdrops on the Hedera network +// @dev Implements HRC-904 standard for cancelling pending airdrops +// +// Pending airdrops can be cancelled in these cases: +// - When receiver has "receiver signature required" enabled +// - When receiver has no available auto-association slots +// - Before the receiver claims the airdrop contract CancelAirdrop is HederaTokenService { + // @notice Cancels a pending fungible token airdrop from a sender to a receiver + // @dev Builds pending airdrop struct and submits cancelAirdrops system contract call + // @param sender The address that sent the tokens + // @param receiver The address that was to receive the tokens + // @param token The token address of the pending airdrop + // @return responseCode The response code from the cancel operation (22 = success) function cancelAirdrop(address sender, address receiver, address token) public returns(int64 responseCode){ IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](1); @@ -25,6 +39,13 @@ contract CancelAirdrop is HederaTokenService { return responseCode; } + // @notice Cancels a pending non-fungible token airdrop from a sender to a receiver + // @dev Builds pending NFT airdrop struct and submits cancelAirdrops system contract call + // @param sender The address that sent the NFT + // @param receiver The address that was to receive the NFT + // @param token The NFT token address + // @param serial The serial number of the NFT + // @return responseCode The response code from the cancel operation (22 = success) function cancelNFTAirdrop(address sender, address receiver, address token, int64 serial) public returns(int64 responseCode){ IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](1); @@ -43,6 +64,13 @@ contract CancelAirdrop is HederaTokenService { return responseCode; } + // @notice Cancels multiple pending airdrops in a single transaction + // @dev Builds array of pending airdrop structs and submits batch cancelAirdrops call + // @param senders Array of addresses that sent the tokens/NFTs + // @param receivers Array of addresses that were to receive the tokens/NFTs + // @param tokens Array of token addresses for the pending airdrops + // @param serials Array of serial numbers for NFT airdrops (use 0 for fungible tokens) + // @return responseCode The response code from the batch cancel operation (22 = success) function cancelAirdrops(address[] memory senders, address[] memory receivers, address[] memory tokens, int64[] memory serials) public returns (int64 responseCode) { uint length = senders.length; IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](length); diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/ClaimAirdrop.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/ClaimAirdrop.sol index bb265809c..29c33ea55 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/ClaimAirdrop.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/ClaimAirdrop.sol @@ -6,8 +6,22 @@ import {HederaTokenService} from "../../HederaTokenService.sol"; import {IHederaTokenService} from "../../IHederaTokenService.sol"; pragma experimental ABIEncoderV2; +// @title Claim Airdrop Contract +// @notice Facilitates claiming of pending token airdrops on the Hedera network +// @dev Implements HRC-904 standard for claiming pending airdrops +// +// Pending airdrops can be claimed in these cases: +// - When receiver has "receiver signature required" enabled +// - When receiver has no available auto-association slots +// - After the sender has airdropped tokens but before they are cancelled contract ClaimAirdrop is HederaTokenService { + // @notice Claims a pending fungible token airdrop sent to the caller + // @dev Builds pending airdrop struct and submits claimAirdrops system contract call + // @param sender The address that sent the tokens + // @param receiver The address claiming the tokens + // @param token The token address of the pending airdrop + // @return responseCode The response code from the claim operation (22 = success) function claim(address sender, address receiver, address token) public returns(int64 responseCode){ IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](1); @@ -25,6 +39,13 @@ contract ClaimAirdrop is HederaTokenService { return responseCode; } + // @notice Claims a pending non-fungible token airdrop sent to the caller + // @dev Builds pending NFT airdrop struct and submits claimAirdrops system contract call + // @param sender The address that sent the NFT + // @param receiver The address claiming the NFT + // @param token The NFT token address + // @param serial The serial number of the NFT + // @return responseCode The response code from the claim operation (22 = success) function claimNFTAirdrop(address sender, address receiver, address token, int64 serial) public returns(int64 responseCode){ IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](1); @@ -43,6 +64,13 @@ contract ClaimAirdrop is HederaTokenService { return responseCode; } + // @notice Claims multiple pending airdrops in a single transaction + // @dev Builds array of pending airdrop structs and submits batch claimAirdrops call + // @param senders Array of addresses that sent the tokens/NFTs + // @param receivers Array of addresses claiming the tokens/NFTs + // @param tokens Array of token addresses for the pending airdrops + // @param serials Array of serial numbers for NFT airdrops (use 0 for fungible tokens) + // @return responseCode The response code from the batch claim operation (22 = success) function claimAirdrops(address[] memory senders, address[] memory receivers, address[] memory tokens, int64[] memory serials) public returns (int64 responseCode) { uint length = senders.length; IHederaTokenService.PendingAirdrop[] memory pendingAirdrops = new IHederaTokenService.PendingAirdrop[](length); diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/HRC904Contract.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/HRC904Contract.sol index a769c3060..6aee1d83f 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/HRC904Contract.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/HRC904Contract.sol @@ -5,45 +5,102 @@ import {IHRC719} from "../../IHRC719.sol"; import {IHRC904TokenFacade} from "../../IHRC904TokenFacade.sol"; import {IHRC904AccountFacade} from "../../../hedera-account-service/IHRC904AccountFacade.sol"; +// @title HRC904 Contract +// @notice Provides interface for token airdrop, claim, reject and association operations +// @dev Implements HRC-904 standard for token management operations +// +// Supports the following token operations: +// - Cancelling pending airdrops for fungible and non-fungible tokens +// - Claiming pending airdrops for fungible and non-fungible tokens +// - Rejecting tokens and NFTs +// - Managing token associations and auto-association settings contract HRC904Contract { event IsAssociated(bool status); + // @notice Cancels a pending fungible token airdrop to a receiver + // @dev Calls cancelAirdropFT on the token's HRC904 facade + // @param token The token address to cancel airdrop for + // @param receiver The address that was to receive the tokens + // @return responseCode The response code from the cancel operation (22 = success) function cancelAirdropFT(address token, address receiver) public returns (int64 responseCode) { return IHRC904TokenFacade(token).cancelAirdropFT(receiver); } + // @notice Cancels a pending non-fungible token airdrop to a receiver + // @dev Calls cancelAirdropNFT on the token's HRC904 facade + // @param token The NFT token address + // @param receiver The address that was to receive the NFT + // @param serial The serial number of the NFT + // @return responseCode The response code from the cancel operation (22 = success) function cancelAirdropNFT(address token, address receiver, int64 serial) public returns (int64 responseCode) { return IHRC904TokenFacade(token).cancelAirdropNFT(receiver, serial); } + // @notice Claims a pending fungible token airdrop from a sender + // @dev Calls claimAirdropFT on the token's HRC904 facade + // @param token The token address to claim airdrop from + // @param sender The address that sent the tokens + // @return responseCode The response code from the claim operation (22 = success) function claimAirdropFT(address token, address sender) public returns (int64 responseCode) { return IHRC904TokenFacade(token).claimAirdropFT(sender); } + // @notice Claims a pending non-fungible token airdrop from a sender + // @dev Calls claimAirdropNFT on the token's HRC904 facade + // @param token The NFT token address + // @param sender The address that sent the NFT + // @param serial The serial number of the NFT + // @return responseCode The response code from the claim operation (22 = success) function claimAirdropNFT(address token, address sender, int64 serial) public returns (int64 responseCode) { return IHRC904TokenFacade(token).claimAirdropNFT(sender, serial); } + // @notice Rejects a fungible token + // @dev Calls rejectTokenFT on the token's HRC904 facade + // @param token The token address to reject + // @return responseCode The response code from the reject operation (22 = success) function rejectTokenFT(address token) public returns (int64 responseCode) { return IHRC904TokenFacade(token).rejectTokenFT(); } + // @notice Rejects multiple non-fungible tokens + // @dev Calls rejectTokenNFTs on the token's HRC904 facade + // @param token The NFT token address + // @param serialNumbers Array of NFT serial numbers to reject + // @return responseCode The response code from the reject operation (22 = success) function rejectTokenNFTs(address token, int64[] memory serialNumbers) public returns (int64 responseCode) { return IHRC904TokenFacade(token).rejectTokenNFTs(serialNumbers); } + // @notice Sets whether an account can have unlimited automatic token associations + // @dev Calls setUnlimitedAutomaticAssociations on the account's HRC904 facade + // @param account The account address to modify + // @param enableAutoAssociations True to enable unlimited associations, false to disable + // @return responseCode The response code from the operation (22 = success) function setUnlimitedAssociations(address account, bool enableAutoAssociations) public returns (int64 responseCode) { return IHRC904AccountFacade(account).setUnlimitedAutomaticAssociations(enableAutoAssociations); } + // @notice Associates the caller's account with a token + // @dev Calls associate on the HRC719 token interface + // @param token The token address to associate with + // @return responseCode The response code from the associate operation (22 = success) function associate(address token) public returns (uint256 responseCode) { return IHRC719(token).associate(); } + // @notice Dissociates the caller's account from a token + // @dev Calls dissociate on the HRC719 token interface + // @param token The token address to dissociate from + // @return responseCode The response code from the dissociate operation (22 = success) function dissociate(address token) public returns (uint256 responseCode) { return IHRC719(token).dissociate(); } + // @notice Checks if the caller's account is associated with a token + // @dev Calls isAssociated on the HRC719 token interface + // @param token The token address to check association status + // @return associated True if the account is associated with the token function isAssociated(address token) public view returns (bool associated) { return IHRC719(token).isAssociated(); } diff --git a/contracts/system-contracts/hedera-token-service/examples/hrc-904/TokenReject.sol b/contracts/system-contracts/hedera-token-service/examples/hrc-904/TokenReject.sol index 11ccca6a1..5cdd3216f 100644 --- a/contracts/system-contracts/hedera-token-service/examples/hrc-904/TokenReject.sol +++ b/contracts/system-contracts/hedera-token-service/examples/hrc-904/TokenReject.sol @@ -6,8 +6,22 @@ import {HederaTokenService} from "../../HederaTokenService.sol"; import {IHederaTokenService} from "../../IHederaTokenService.sol"; pragma experimental ABIEncoderV2; +// @title Token Reject Contract +// @notice Facilitates rejection of both fungible and non-fungible tokens on the Hedera network +// @dev Implements token rejection functionality by building NFT structs and calling the rejectTokens system contract +// +// Allows accounts to reject: +// - Multiple fungible tokens in a single transaction +// - Multiple NFTs in a single transaction +// - Both fungible and non-fungible tokens together contract TokenReject is HederaTokenService { + // @notice Rejects multiple fungible and non-fungible tokens for an account + // @dev Builds NFT ID structs and submits rejectTokens system contract call + // @param rejectingAddress The address rejecting the tokens + // @param ftAddresses Array of fungible token addresses to reject + // @param nftAddresses Array of NFT token addresses to reject + // @return responseCode The response code from the reject operation (22 = success) function rejectTokens(address rejectingAddress, address[] memory ftAddresses, address[] memory nftAddresses) public returns(int64 responseCode) { IHederaTokenService.NftID[] memory nftIDs = new IHederaTokenService.NftID[](nftAddresses.length); for (uint i; i < nftAddresses.length; i++) diff --git a/test/constants.js b/test/constants.js index 0e0fedef4..ab396427a 100644 --- a/test/constants.js +++ b/test/constants.js @@ -224,7 +224,7 @@ const HOUR = 60 * MINUTE; const DAY = 24 * HOUR; const WEEK = 7 * DAY; const GWEI = 1e9; -const PRECOMPILE_ADDRESS = '0.0.359'; +const HTS_SYSTEM_CONTRACT_ADDRESS = '0.0.359'; module.exports = { Events, @@ -247,5 +247,5 @@ module.exports = { WEEK, WEI, GWEI, - PRECOMPILE_ADDRESS, + HTS_SYSTEM_CONTRACT_ADDRESS, }; diff --git a/test/system-contracts/hedera-token-service/utils.js b/test/system-contracts/hedera-token-service/utils.js index 4849e2159..39d8618d0 100644 --- a/test/system-contracts/hedera-token-service/utils.js +++ b/test/system-contracts/hedera-token-service/utils.js @@ -973,7 +973,7 @@ class Utils { `${mirrorNodeUrl}/contracts/results/${txHash}/actions` ); const precompileAction = res.data.actions.find( - (x) => x.recipient === Constants.PRECOMPILE_ADDRESS + (x) => x.recipient === Constants.HTS_SYSTEM_CONTRACT_ADDRESS ); return BigInt(precompileAction.result_data).toString(); }