From 95d6f6a1a77ca65ae572903f5fa65b5acbc8924e Mon Sep 17 00:00:00 2001 From: Logan Nguyen Date: Mon, 11 Sep 2023 11:58:05 -0500 Subject: [PATCH] dapp-feat: TransferToken feature complete (#319) (#382) * dapp-update: added gasLimit to TransferSingleToken API Signed-off-by: Logan Nguyen * dapp-update: updated util component for TransferSingleToken Signed-off-by: Logan Nguyen * dapp-feat: finished TransferSingleToken feature Signed-off-by: Logan Nguyen * dapp-update: merged transferSignleToken and transferSignleTokenFrom into 1 API Signed-off-by: Logan Nguyen * dapp-feat: TransferSingleTokenFrom Feature complete (#318) Signed-off-by: Logan Nguyen --------- Signed-off-by: Logan Nguyen --- .../token-transfer-contract/index.test.ts | 63 +++-- .../tokenTransfer-interactions/index.ts | 131 ++++----- .../components/CryptoTransferInputFields.tsx | 3 +- .../components/TokenPermissionInfoModal.tsx | 5 +- .../components/TransactionResultTable.tsx | 71 ++++- .../hts/shared/methods/handleAPIErrors.tsx | 3 + .../methods/handleSanitizeFormInputs.tsx | 47 ++-- .../token-transfer-contract/method/index.tsx | 4 +- .../transferCrypto/TokenTransferForm.tsx | 3 +- .../method/transferSingleToken/index.tsx | 253 ++++++++++++++++++ .../contract-interactions/HTS/index.d.ts | 1 + .../src/utils/common/constants.ts | 2 +- .../HTS/token-transfer/paramFieldConstant.ts | 64 ++++- 13 files changed, 503 insertions(+), 147 deletions(-) create mode 100644 system-contract-dapp-playground/src/components/contract-interaction/hts/token-transfer-contract/method/transferSingleToken/index.tsx diff --git a/system-contract-dapp-playground/__tests__/hedera/hts-interactions/token-transfer-contract/index.test.ts b/system-contract-dapp-playground/__tests__/hedera/hts-interactions/token-transfer-contract/index.test.ts index bf874e21c..2bca1fbd9 100644 --- a/system-contract-dapp-playground/__tests__/hedera/hts-interactions/token-transfer-contract/index.test.ts +++ b/system-contract-dapp-playground/__tests__/hedera/hts-interactions/token-transfer-contract/index.test.ts @@ -23,7 +23,6 @@ import { transferFungibleTokens, transferNonFungibleTokens, transferSingleToken, - transferSingleTokenFrom, } from '@/api/hedera/tokenTransfer-interactions'; import { IHederaTokenServiceTokenTransferList, @@ -245,7 +244,8 @@ describe('TokenTransferContract test suite', () => { hederaTokenAddress, senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBeNull; @@ -260,7 +260,8 @@ describe('TokenTransferContract test suite', () => { hederaTokenAddress, senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBeNull; @@ -275,7 +276,8 @@ describe('TokenTransferContract test suite', () => { '0xabc', senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid token address'); @@ -290,7 +292,8 @@ describe('TokenTransferContract test suite', () => { hederaTokenAddress, '0xabc', receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid sender address'); @@ -305,7 +308,8 @@ describe('TokenTransferContract test suite', () => { hederaTokenAddress, senderA, '0xabc', - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid receiver address'); @@ -320,7 +324,8 @@ describe('TokenTransferContract test suite', () => { hederaTokenAddress, senderA, receiverB, - -369 + -369, + gasLimit ); expect(txRes.err).toBe('Invalid quantity'); @@ -331,13 +336,14 @@ describe('TokenTransferContract test suite', () => { describe('transferSingleTokenFrom test suite', () => { it('should execute transferSingleTokenFrom with API === "FUNGIBLE" then return a successful response code', async () => { - const txRes = await transferSingleTokenFrom( + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'FUNGIBLE', + 'FUNGIBLE_FROM', hederaTokenAddress, senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBeNull; @@ -345,14 +351,15 @@ describe('TokenTransferContract test suite', () => { expect(txRes.transactionHash).toBe(txHash); }); - it('should execute transferSingleTokenFrom with API === "NON_FUNGIBLE" then return a successful response code', async () => { - const txRes = await transferSingleTokenFrom( + it('should execute transferSingleTokenFrom with API === "NFT_FROM" then return a successful response code', async () => { + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'NON_FUNGIBLE', + 'NFT_FROM', hederaTokenAddress, senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBeNull; @@ -361,13 +368,14 @@ describe('TokenTransferContract test suite', () => { }); it('should execute transferSingleTokenFrom with an invalid token address then return an error', async () => { - const txRes = await transferSingleTokenFrom( + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'FUNGIBLE', + 'FUNGIBLE_FROM', '0xabc', senderA, receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid token address'); @@ -376,13 +384,14 @@ describe('TokenTransferContract test suite', () => { }); it('should execute transferSingleTokenFrom with an invalid sender accountID then return an error', async () => { - const txRes = await transferSingleTokenFrom( + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'FUNGIBLE', + 'FUNGIBLE_FROM', hederaTokenAddress, '0xabc', receiverA, - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid sender address'); @@ -391,13 +400,14 @@ describe('TokenTransferContract test suite', () => { }); it('should execute transferSingleTokenFrom with an invalid receiver accountID then return an error', async () => { - const txRes = await transferSingleTokenFrom( + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'FUNGIBLE', + 'FUNGIBLE_FROM', hederaTokenAddress, senderA, '0xabc', - 369 + 369, + gasLimit ); expect(txRes.err).toBe('Invalid receiver address'); @@ -406,13 +416,14 @@ describe('TokenTransferContract test suite', () => { }); it('should execute transferSingleTokenFrom with an invalid quantity then return an error', async () => { - const txRes = await transferSingleTokenFrom( + const txRes = await transferSingleToken( baseContract as unknown as Contract, - 'FUNGIBLE', + 'FUNGIBLE_FROM', hederaTokenAddress, senderA, receiverB, - -369 + -369, + gasLimit ); expect(txRes.err).toBe('Invalid quantity'); diff --git a/system-contract-dapp-playground/src/api/hedera/tokenTransfer-interactions/index.ts b/system-contract-dapp-playground/src/api/hedera/tokenTransfer-interactions/index.ts index ea07482fb..4d8c47160 100644 --- a/system-contract-dapp-playground/src/api/hedera/tokenTransfer-interactions/index.ts +++ b/system-contract-dapp-playground/src/api/hedera/tokenTransfer-interactions/index.ts @@ -199,80 +199,13 @@ export const transferNonFungibleTokens = async ( * * @dev integrates TokenTransferContract.transferNFTPublic() * - * @param baseContract: ethers.Contract - * - * @param API: "FUNGIBLE" | "NON_FUNGIBLE" - * - * @param hederaTokenAddress: string - * - * @param sender: string - * - * @param receiver: string - * - * @param quantity: number (amount/serialNumber) - * - * @return Promise - */ -export const transferSingleToken = async ( - baseContract: Contract, - API: 'FUNGIBLE' | 'NON_FUNGIBLE', - hederaTokenAddress: string, - sender: string, - receiver: string, - quantity: number -): Promise => { - // sanitize params - let sanitizeErr; - if (!isAddress(hederaTokenAddress)) { - sanitizeErr = 'Invalid token address'; - } else if (!isAddress(sender)) { - sanitizeErr = 'Invalid sender address'; - } else if (!isAddress(receiver)) { - sanitizeErr = 'Invalid receiver address'; - } else if (quantity < 0) { - sanitizeErr = 'Invalid quantity'; - } - if (sanitizeErr) { - console.error(sanitizeErr); - return { err: sanitizeErr }; - } - - // invoking contract methods - try { - let transactionResult; - if (API === 'FUNGIBLE') { - transactionResult = await baseContract.transferTokenPublic( - hederaTokenAddress, - sender, - receiver, - quantity - ); - } else { - transactionResult = await baseContract.transferNFTPublic( - hederaTokenAddress, - sender, - receiver, - quantity - ); - } - - return await handleContractResponse(transactionResult); - } catch (err: any) { - console.error(err); - return { err, transactionHash: err.receipt && err.receipt.hash }; - } -}; - -/** - * @dev transfers single token from token owner (fungible vs non-fungible) - * * @dev integrates TokenTransferContract.transferFromPublic() * * @dev integrates TokenTransferContract.transferFromNFTPublic() * * @param baseContract: ethers.Contract * - * @param API: "FUNGIBLE" | "NON_FUNGIBLE" + * @param API: "FUNGIBLE" | "NON_FUNGIBLE" | 'FUNGIBLE_FROM' | 'NFT_FROM' * * @param hederaTokenAddress: string * @@ -284,13 +217,14 @@ export const transferSingleToken = async ( * * @return Promise */ -export const transferSingleTokenFrom = async ( +export const transferSingleToken = async ( baseContract: Contract, - API: 'FUNGIBLE' | 'NON_FUNGIBLE', + API: 'FUNGIBLE' | 'NON_FUNGIBLE' | 'FUNGIBLE_FROM' | 'NFT_FROM', hederaTokenAddress: string, sender: string, receiver: string, - quantity: number + quantity: number, + gasLimit: number ): Promise => { // sanitize params let sanitizeErr; @@ -311,20 +245,47 @@ export const transferSingleTokenFrom = async ( // invoking contract methods try { let transactionResult; - if (API === 'FUNGIBLE') { - transactionResult = await baseContract.transferFromPublic( - hederaTokenAddress, - sender, - receiver, - quantity - ); - } else { - transactionResult = await baseContract.transferFromNFTPublic( - hederaTokenAddress, - sender, - receiver, - quantity - ); + + switch (API) { + case 'FUNGIBLE': + transactionResult = await baseContract.transferTokenPublic( + hederaTokenAddress, + sender, + receiver, + quantity, + { gasLimit } + ); + break; + + case 'NON_FUNGIBLE': + transactionResult = await baseContract.transferNFTPublic( + hederaTokenAddress, + sender, + receiver, + quantity, + { gasLimit } + ); + break; + + case 'FUNGIBLE_FROM': + transactionResult = await baseContract.transferFromPublic( + hederaTokenAddress, + sender, + receiver, + quantity, + { gasLimit } + ); + break; + + case 'NFT_FROM': + transactionResult = await baseContract.transferFromNFTPublic( + hederaTokenAddress, + sender, + receiver, + quantity, + { gasLimit } + ); + break; } return await handleContractResponse(transactionResult); diff --git a/system-contract-dapp-playground/src/components/contract-interaction/hts/shared/components/CryptoTransferInputFields.tsx b/system-contract-dapp-playground/src/components/contract-interaction/hts/shared/components/CryptoTransferInputFields.tsx index 77e92caa8..ddee85ce8 100644 --- a/system-contract-dapp-playground/src/components/contract-interaction/hts/shared/components/CryptoTransferInputFields.tsx +++ b/system-contract-dapp-playground/src/components/contract-interaction/hts/shared/components/CryptoTransferInputFields.tsx @@ -19,6 +19,7 @@ */ import { Tooltip, Select } from '@chakra-ui/react'; +import { HEDERA_BRANDING_COLORS } from '@/utils/common/constants'; import { SharedFormInputField, SharedRemoveFieldsButton } from './ParamInputForm'; import { htsCryptoTransferParamFields } from '@/utils/contract-interactions/HTS/token-transfer/paramFieldConstant'; @@ -85,7 +86,7 @@ const CryptoTransferInputFields = ({ fontWeight={'medium'} > diff --git a/system-contract-dapp-playground/src/components/contract-interaction/hts/token-transfer-contract/method/transferSingleToken/index.tsx b/system-contract-dapp-playground/src/components/contract-interaction/hts/token-transfer-contract/method/transferSingleToken/index.tsx new file mode 100644 index 000000000..04bb81979 --- /dev/null +++ b/system-contract-dapp-playground/src/components/contract-interaction/hts/token-transfer-contract/method/transferSingleToken/index.tsx @@ -0,0 +1,253 @@ +/*- + * + * Hedera Smart Contracts + * + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import Cookies from 'js-cookie'; +import { Contract } from 'ethers'; +import { useToast } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; +import { CommonErrorToast } from '@/components/toast/CommonToast'; +import { TransactionResult } from '@/types/contract-interactions/HTS'; +import { handleAPIErrors } from '../../../shared/methods/handleAPIErrors'; +import { TRANSACTION_PAGE_SIZE } from '../../../shared/states/commonStates'; +import { useToastSuccessful } from '../../../shared/hooks/useToastSuccessful'; +import { transferSingleToken } from '@/api/hedera/tokenTransfer-interactions'; +import { usePaginatedTxResults } from '../../../shared/hooks/usePaginatedTxResults'; +import { TransactionResultTable } from '../../../shared/components/TransactionResultTable'; +import { handleSanitizeHederaFormInputs } from '../../../shared/methods/handleSanitizeFormInputs'; +import { useUpdateTransactionResultsToLocalStorage } from '../../../shared/hooks/useUpdateLocalStorage'; +import { htsTokenTransferParamFields } from '@/utils/contract-interactions/HTS/token-transfer/paramFieldConstant'; +import { handleRetrievingTransactionResultsFromLocalStorage } from '../../../shared/methods/handleRetrievingTransactionResultsFromLocalStorage'; +import { + SharedExecuteButton, + SharedFormInputField, +} from '../../../shared/components/ParamInputForm'; + +interface PageProps { + baseContract: Contract; +} + +type API_NAMES = 'FUNGIBLE' | 'NON_FUNGIBLE' | 'FUNGIBLE_FROM' | 'NFT_FROM'; + +const TransferSingleToken = ({ baseContract }: PageProps) => { + // general states + const toaster = useToast(); + const [isSuccessful, setIsSuccessful] = useState(false); + const hederaNetwork = JSON.parse(Cookies.get('_network') as string); + const [currentTransactionPage, setCurrentTransactionPage] = useState(1); + const transactionResultStorageKey = 'HEDERA.HTS.TOKEN-TRANSFER.SINGLE-TOKEN-RESULTS'; + const [transactionResults, setTransactionResults] = useState([]); + const tokenInfoFields = [ + 'hederaTokenAddress', + 'senderAddress', + 'receiverAddress', + 'quantity', + 'feeValue', + ]; + const [isLoading, setIsLoading] = useState({ + FUNGIBLE: false, + NON_FUNGIBLE: false, + FUNGIBLE_FROM: false, + NFT_FROM: false, + }); + const APIButtonTitles: { API: API_NAMES; executeTitle: string }[] = [ + { API: 'FUNGIBLE', executeTitle: 'Transfer Fungible' }, + { API: 'NON_FUNGIBLE', executeTitle: 'Transfer NFT' }, + { API: 'FUNGIBLE_FROM', executeTitle: 'Transfer Fungible From' }, + { API: 'NFT_FROM', executeTitle: 'Transfer NFT From' }, + ]; + const initialParamValues = { + feeValue: '', + quantity: '', + senderAddress: '', + receiverAddress: '', + hederaTokenAddress: '', + }; + const [paramValues, setParamValues] = useState(initialParamValues); + + /** @dev retrieve token creation results from localStorage to maintain data on re-renders */ + useEffect(() => { + handleRetrievingTransactionResultsFromLocalStorage( + toaster, + transactionResultStorageKey, + setCurrentTransactionPage, + setTransactionResults + ); + }, [toaster]); + + // declare a paginatedTransactionResults + const paginatedTransactionResults = usePaginatedTxResults( + currentTransactionPage, + transactionResults + ); + + /** @dev handle form inputs on change */ + const handleInputOnChange = (e: any, param: string) => { + setParamValues((prev: any) => ({ ...prev, [param]: e.target.value })); + }; + + /** @dev handle invoking the API to interact with smart contract and transfer token */ + const handleTransferSingileToken = async (API: API_NAMES) => { + // destructuring params + const { hederaTokenAddress, senderAddress, receiverAddress, quantity, feeValue } = paramValues; + + // sanitize params + const sanitizeErr = handleSanitizeHederaFormInputs({ + API: 'TransferSingle', + hederaTokenAddress, + senderAddress, + receiverAddress, + amount: quantity, + feeValue, + }); + + // toast error if any param is invalid + if (sanitizeErr) { + CommonErrorToast({ toaster, title: 'Invalid parameters', description: sanitizeErr }); + return; + } + + // turn isLoading on + setIsLoading((prev) => ({ ...prev, [API]: true })); + + // invoke method API + const { result, transactionHash, err } = await transferSingleToken( + baseContract, + API, + hederaTokenAddress, + senderAddress, + receiverAddress, + Number(quantity), + Number(feeValue) + ); + + // turn isLoading off + setIsLoading((prev) => ({ ...prev, [API]: false })); + + // handle err + if (err || !result) { + handleAPIErrors({ + err, + toaster, + receiverAddress, + APICalled: API, + transactionHash, + setTransactionResults, + accountAddress: senderAddress, + tokenAddress: paramValues.hederaTokenAddress, + }); + return; + } else { + // handle succesfull + setTransactionResults((prev) => [ + ...prev, + { + APICalled: API, + receiverAddress, + status: 'success', + accountAddress: senderAddress, + txHash: transactionHash as string, + tokenAddress: paramValues.hederaTokenAddress, + }, + ]); + + setIsSuccessful(true); + } + }; + + /** @dev listen to change event on transactionResults state => load to localStorage */ + useUpdateTransactionResultsToLocalStorage(transactionResults, transactionResultStorageKey); + + /** @dev toast successful */ + useToastSuccessful({ + toaster, + isSuccessful, + setParamValues, + setIsSuccessful, + transactionResults, + setCurrentTransactionPage, + resetParamValues: initialParamValues, + toastTitle: 'Token transfer successful', + }); + + return ( +
+ {/* Transfer Token form */} +
+ {/* field inputs */} + {tokenInfoFields.map((param) => ( +
+ +
+ ))} + + {/* Execute buttons */} +
+ {APIButtonTitles.slice(0, 2).map((APIButton) => ( + handleTransferSingileToken(APIButton.API)} + buttonTitle={APIButton.executeTitle} + /> + ))} +
+ +
+ {APIButtonTitles.slice(2).map((APIButton) => ( + handleTransferSingileToken(APIButton.API)} + buttonTitle={APIButton.executeTitle} + /> + ))} +
+ + {/* transaction results table */} + {transactionResults.length > 0 && ( + + )} +
+
+ ); +}; + +export default TransferSingleToken; diff --git a/system-contract-dapp-playground/src/types/contract-interactions/HTS/index.d.ts b/system-contract-dapp-playground/src/types/contract-interactions/HTS/index.d.ts index 136d76080..bb3d84eaf 100644 --- a/system-contract-dapp-playground/src/types/contract-interactions/HTS/index.d.ts +++ b/system-contract-dapp-playground/src/types/contract-interactions/HTS/index.d.ts @@ -29,6 +29,7 @@ export type TransactionResult = { tokenAddress?: string; accountAddress?: string; tokenAddresses?: string[]; + receiverAddress?: string; }; /** @dev an interface for the results returned back from interacting with Hedera System Smart Contracts */ diff --git a/system-contract-dapp-playground/src/utils/common/constants.ts b/system-contract-dapp-playground/src/utils/common/constants.ts index 7c2a3900c..1ee56f878 100644 --- a/system-contract-dapp-playground/src/utils/common/constants.ts +++ b/system-contract-dapp-playground/src/utils/common/constants.ts @@ -231,7 +231,7 @@ export const HEDERA_SMART_CONTRACTS_ASSETS = { contractABI: TokenTransferContract.abi, contractBytecode: TokenTransferContract.bytecode, githubUrl: `${HEDERA_SMART_CONTRACT_OFFICIAL_GITHUB_URL}/blob/main/contracts/hts-precompile/examples/token-transfer/TokenTransferContract.sol`, - methods: ['crypto', 'transferToken', 'transferTokens', 'transferFrom'], + methods: ['crypto', 'transferToken', 'transferTokens'], }, ], TOKEN_ASSOCIATION: { diff --git a/system-contract-dapp-playground/src/utils/contract-interactions/HTS/token-transfer/paramFieldConstant.ts b/system-contract-dapp-playground/src/utils/contract-interactions/HTS/token-transfer/paramFieldConstant.ts index 53aadef1e..c3b3f5b09 100644 --- a/system-contract-dapp-playground/src/utils/contract-interactions/HTS/token-transfer/paramFieldConstant.ts +++ b/system-contract-dapp-playground/src/utils/contract-interactions/HTS/token-transfer/paramFieldConstant.ts @@ -18,13 +18,15 @@ * */ +import { HEDERA_BRANDING_COLORS } from '@/utils/common/constants'; + /** @notice an object holding information for the queryTokenInfo's input fields */ export const htsCryptoTransferParamFields = { accountID: { inputType: 'text', inputPlaceholder: 'Account ID...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'accountID', explanation: 'represents the accountID that sends/receives cryptocurrency or tokens', @@ -33,7 +35,7 @@ export const htsCryptoTransferParamFields = { inputType: 'number', inputPlaceholder: 'Amount...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'amount', explanation: @@ -48,7 +50,7 @@ export const htsCryptoTransferParamFields = { inputType: 'text', inputPlaceholder: 'Token address...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'hederaTokenAddress', explanation: 'represents the Hedera Token address', @@ -57,7 +59,7 @@ export const htsCryptoTransferParamFields = { inputType: 'text', inputPlaceholder: 'Sender ID...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'senderAccountID', explanation: 'represents the accountID of the sender', @@ -66,7 +68,7 @@ export const htsCryptoTransferParamFields = { inputType: 'text', inputPlaceholder: 'Receiver ID...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'receiverAccountID', explanation: 'represents the accountID of the receiver', @@ -75,7 +77,7 @@ export const htsCryptoTransferParamFields = { inputType: 'text', inputPlaceholder: 'Serial number...', inputSize: 'md', - inputFocusBorderColor: '#A98DF4', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, inputClassname: 'w-full border-white/30', paramKey: 'serialNumber', explanation: 'represents the serial number of the NFT', @@ -86,3 +88,53 @@ export const htsCryptoTransferParamFields = { 'If true then the transfer is expected to be an approved allowance and the senderAccountID is expected to be the owner. The default is false (omitted).', }, }; + +/** @notice an object holding information for the tokenTransfer's input fields */ +export const htsTokenTransferParamFields = { + hederaTokenAddress: { + inputType: 'text', + inputPlaceholder: 'Token address...', + inputSize: 'md', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, + inputClassname: 'w-full border-white/30', + paramKey: 'hederaTokenAddress', + explanation: 'represents the Hedera token to be transfered', + }, + senderAddress: { + inputType: 'text', + inputPlaceholder: 'Sender address...', + inputSize: 'md', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, + inputClassname: 'w-full border-white/30', + paramKey: 'senderAddress', + explanation: 'represents the sender address', + }, + receiverAddress: { + inputType: 'text', + inputPlaceholder: 'Receiver address...', + inputSize: 'md', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, + inputClassname: 'w-full border-white/30', + paramKey: 'receiverAddress', + explanation: 'represents the receiver address', + }, + quantity: { + inputType: 'number', + inputPlaceholder: 'Amount | Serial number...', + inputSize: 'md', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, + inputClassname: 'w-full border-white/30', + paramKey: 'quantity', + explanation: + 'represents the amount for type FUNGIBLE_COMMON and serial number for type NON_FUNGIBLE_COMMON', + }, + feeValue: { + inputType: 'number', + inputPlaceholder: 'Gas limit...', + inputSize: 'md', + inputFocusBorderColor: HEDERA_BRANDING_COLORS.purple, + inputClassname: 'w-full border-white/30', + paramKey: 'feeValue', + explanation: 'represents the gas limit for the transaction', + }, +};