Skip to content

Commit

Permalink
dapp-feat: CryptoTransfer feature complete (#319) (#381)
Browse files Browse the repository at this point in the history
* dapp-feat: added TokenTransferContract to the main layout

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-update: added gasLimist to to transferCrypto API method

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-feat: added helper functions and form components for CryptoTransfer master component

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-update: updated util component for new CryptoTransfer API

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-feat: CryptoTransfer feature complete (#319)

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-update: added guard condition to hide `cryptoTransfer` method if the network !== "localnet"

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-update: added guard condition to hide `cryptoTransfer` method if the network !== "localnet"

Signed-off-by: Logan Nguyen <[email protected]>

* dapp-update: updated TokenTransferContract's method tabs' names

Signed-off-by: Logan Nguyen <[email protected]>

---------

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Sep 8, 2023
1 parent 28ffb5e commit 3457154
Show file tree
Hide file tree
Showing 16 changed files with 1,414 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { Contract } from 'ethers';

describe('TokenTransferContract test suite', () => {
const responseCode = 22;
const gasLimit = 1000000;
const invalidSender = '0xabc';
const senderA = '0xDd7fCb7c2ee96A79B1e201d25F5E43d6a0cED5e6';
const senderB = '0x0851072d7bB726305032Eff23CB8fd22eB74c85B';
Expand Down Expand Up @@ -82,23 +83,26 @@ describe('TokenTransferContract test suite', () => {
// prepare tokenTransferList: IHederaTokenServiceTokenTransferList
const nftTransfers = [
{
senderAcocuntID: senderA,
senderAccountID: senderA,
receiverAccountID: receiverA,
serialNumber: 3,
isApproval: false,
},
];
const tokenTransferList: IHederaTokenServiceTokenTransferList = {
token: hederaTokenAddress,
transfers,
nftTransfers,
};
const tokenTransferList: IHederaTokenServiceTokenTransferList[] = [
{
token: hederaTokenAddress,
transfers,
nftTransfers,
},
];

it('should execute transferCrypto then return a successful response code', async () => {
const txRes = await transferCrypto(
baseContract as unknown as Contract,
transferList,
tokenTransferList
tokenTransferList,
gasLimit
);

expect(txRes.err).toBeNull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,23 @@ import { handleContractResponse } from '@/utils/contract-interactions/HTS/helper
*
* @param transferList: IHederaTokenServiceTransferList
*
* @param tokenTransferList: IHederaTokenServiceTokenTransferList
* @param tokenTransferList: IHederaTokenServiceTokenTransferList[]
*
* @param gasLimit: number
*
* @return Promise<SmartContractExecutionResult>
*/
export const transferCrypto = async (
baseContract: Contract,
transferList: IHederaTokenServiceTransferList,
tokenTransferList: IHederaTokenServiceTokenTransferList
tokenTransferList: IHederaTokenServiceTokenTransferList[],
gasLimit: number
): Promise<SmartContractExecutionResult> => {
// invoking contract methods
try {
const tx = await baseContract.cryptoTransferPublic(transferList, tokenTransferList);
const tx = await baseContract.cryptoTransferPublic(transferList, tokenTransferList, {
gasLimit,
});

return await handleContractResponse(tx);
} catch (err: any) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*-
*
* 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 { Tooltip, Select } from '@chakra-ui/react';
import { SharedFormInputField, SharedRemoveFieldsButton } from './ParamInputForm';
import { htsCryptoTransferParamFields } from '@/utils/contract-interactions/HTS/token-transfer/paramFieldConstant';

interface PageProps {
mode: 'CRYPTO' | 'TOKEN';
paramValue: any;
masterTokenParam?: any;
handleModifyRecords: any;
setCryptoTransferParamValues?: any;
handleTokenTransferInputOnChange?: any;
handleCryptoTransferInputOnChange?: any;
}

const CryptoTransferInputFields = ({
mode,
paramValue,
masterTokenParam,
handleModifyRecords,
setCryptoTransferParamValues,
handleTokenTransferInputOnChange,
handleCryptoTransferInputOnChange,
}: PageProps) => {
return (
<div key={paramValue.fieldKey} className="flex gap-6 items-center w-full">
{/* account ID & amount */}
{(['accountID', 'amount'] as ('accountID' | 'amount')[]).map((paramKey) => (
<SharedFormInputField
key={paramKey}
param={htsCryptoTransferParamFields[paramKey].paramKey}
paramValue={paramValue.fieldValue[paramKey]}
paramKey={htsCryptoTransferParamFields[paramKey].paramKey}
paramType={htsCryptoTransferParamFields[paramKey].inputType}
paramSize={htsCryptoTransferParamFields[paramKey].inputSize}
explanation={htsCryptoTransferParamFields[paramKey].explanation}
paramClassName={htsCryptoTransferParamFields[paramKey].inputClassname}
paramPlaceholder={htsCryptoTransferParamFields[paramKey].inputPlaceholder}
paramFocusColor={htsCryptoTransferParamFields[paramKey].inputFocusBorderColor}
handleInputOnChange={(e) => {
if (mode === 'TOKEN') {
handleTokenTransferInputOnChange(
e,
masterTokenParam.fieldKey,
paramValue.fieldKey,
'transfers',
paramKey
);
} else {
handleCryptoTransferInputOnChange(
e,
paramKey,
paramValue.fieldKey,
setCryptoTransferParamValues
);
}
}}
/>
))}

{/* isApproval A*/}
<div>
<Tooltip
label={htsCryptoTransferParamFields.isApprovalA.explanation}
placement="top"
fontWeight={'medium'}
>
<Select
_focus={{ borderColor: '#A98DF4' }}
className="w-[200px] hover:cursor-pointer rounded-md border-white/30"
placeholder="Approval Status"
onChange={(e) => {
if (mode === 'TOKEN') {
handleTokenTransferInputOnChange(
e,
masterTokenParam.fieldKey,
paramValue.fieldKey,
'transfers',
'isApprovalA'
);
} else {
handleCryptoTransferInputOnChange(
e,
'isApprovalA',
paramValue.fieldKey,
setCryptoTransferParamValues
);
}
}}
>
<option value={'false'}>FALSE</option>
<option value={'true'}>TRUE</option>
</Select>
</Tooltip>
</div>

{/* delete key button */}
<div className="w-fit flex items-center">
<SharedRemoveFieldsButton
fieldKey={paramValue.fieldKey}
handleModifyTokenAddresses={handleModifyRecords}
/>
</div>
</div>
);
};

export default CryptoTransferInputFields;
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { HEDERA_BRANDING_COLORS } from '@/utils/common/constants';
interface SharedFormInputFieldPageProps {
param: string;
paramKey: string;
setFieldKey?: any;
paramType: string;
paramSize: string;
paramValue: string;
Expand All @@ -37,7 +38,7 @@ interface SharedFormInputFieldPageProps {
paramClassName: string;
paramFocusColor: string;
paramPlaceholder: string;
handleInputOnChange: (e: any, param: string, fieldKeyToSet?: string) => void;
handleInputOnChange: (e: any, param: string, fieldKeyToSet?: string, setFieldKey?: any) => void;
}

export const SharedFormInputField = ({
Expand All @@ -48,6 +49,7 @@ export const SharedFormInputField = ({
paramSize,
paramValue,
explanation,
setFieldKey,
fieldKeyToSet,
paramClassName,
paramFocusColor,
Expand All @@ -60,7 +62,7 @@ export const SharedFormInputField = ({
value={paramValue}
disabled={isDisable}
type={paramType}
onChange={(e) => handleInputOnChange(e, param, fieldKeyToSet)}
onChange={(e) => handleInputOnChange(e, param, fieldKeyToSet, setFieldKey)}
placeholder={paramPlaceholder}
size={paramSize}
focusBorderColor={paramFocusColor}
Expand Down Expand Up @@ -147,6 +149,7 @@ export const SharedExecuteButton = ({
/** @dev shared remove fields button */
interface SharedRemoveFieldButtonPageProps {
fieldKey: string;
setFieldKey?: any;
handleModifyTokenAddresses: any;
}
export const SharedRemoveFieldsButton = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ interface TransactionResultTablePageProps {
| 'TokenMint'
| 'TokenCreate'
| 'QueryValidity'
| 'CryptoTransfer'
| 'TokenAssociate'
| 'QueryTokenStatus'
| 'QuerySpecificInfo'
| 'QueryTokenGeneralInfo'
| 'QueryTokenRelation'
| 'QueryTokenPermission'
| 'QueryTokenStatus';
| 'QueryTokenGeneralInfo';
}

export const TransactionResultTable = ({
Expand Down Expand Up @@ -120,7 +122,9 @@ export const TransactionResultTable = ({
</Th>
<Th color={HEDERA_BRANDING_COLORS.violet}>Status</Th>
<Th color={HEDERA_BRANDING_COLORS.violet}>Tx hash</Th>
<Th color={HEDERA_BRANDING_COLORS.violet}>Token address</Th>
{API !== 'CryptoTransfer' && (
<Th color={HEDERA_BRANDING_COLORS.violet}>Token address</Th>
)}
{API === 'TokenMint' && <Th color={HEDERA_BRANDING_COLORS.violet}>Recipient</Th>}
{API === 'TokenAssociate' && (
<Th color={HEDERA_BRANDING_COLORS.violet}>Associated Account</Th>
Expand Down Expand Up @@ -189,11 +193,14 @@ export const TransactionResultTable = ({
<PopoverTrigger>
<div className="flex gap-1 items-center">
<Tooltip label="click to copy transaction hash">
{/* {withTokenAddress ? ( */}
<p>
{transactionResult.txHash.slice(0, beginingHashIndex)}...
{transactionResult.txHash.slice(endingHashIndex)}
</p>
{API === 'CryptoTransfer' ? (
transactionResult.txHash
) : (
<p>
{transactionResult.txHash.slice(0, beginingHashIndex)}...
{transactionResult.txHash.slice(endingHashIndex)}
</p>
)}
</Tooltip>
</div>
</PopoverTrigger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ interface HookProps {
setMetadata?: any;
toastTitle: string;
isSuccessful: boolean;
resetParamValues: any;
resetParamValues?: any;
setTokenAddresses?: any;
toastDescription?: string;
setParamValues: Dispatch<any>;
setParamValues?: Dispatch<any>;
initialTokenAddressesValues?: any;
setTokenTransferParamValues?: any;
setCryptoTransferParamValues?: any;
initialKeyValues?: CommonKeyObject[];
transactionResults: TransactionResult[];
setIsSuccessful: Dispatch<SetStateAction<boolean>>;
Expand Down Expand Up @@ -63,7 +65,9 @@ export const useToastSuccessful = ({
setKeyTypesToShow,
transactionResults,
setCurrentTransactionPage,
setTokenTransferParamValues,
initialTokenAddressesValues,
setCryptoTransferParamValues,
}: HookProps) => {
useEffect(() => {
if (isSuccessful) {
Expand All @@ -78,9 +82,11 @@ export const useToastSuccessful = ({
setIsSuccessful(false);
if (setKeys) setKeys([]);
if (setMetadata) setMetadata([]);
setParamValues(resetParamValues);
if (setWithCustomFee) setWithCustomFee(false);
if (setParamValues) setParamValues(resetParamValues);
if (initialKeyValues && setKeys) setKeys(initialKeyValues);
if (setTokenTransferParamValues) setTokenTransferParamValues([]);
if (setCryptoTransferParamValues) setCryptoTransferParamValues([]);
if (setKeyTypesToShow) setKeyTypesToShow(new Set(HederaTokenKeyTypes));
if (setTokenAddresses) setTokenAddresses([initialTokenAddressesValues]);
if (setChosenKeys) setChosenKeys(new Set<IHederaTokenServiceKeyType>());
Expand All @@ -105,6 +111,8 @@ export const useToastSuccessful = ({
setTokenAddresses,
transactionResults.length,
setCurrentTransactionPage,
setTokenTransferParamValues,
initialTokenAddressesValues,
setCryptoTransferParamValues,
]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*-
*
* 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 { Contract } from 'ethers';
import CryptoTransfer from './transferCrypto';
import { NetworkName } from '@/types/common';

interface PageProps {
method: string;
baseContract: Contract;
}

const HederaTokenTransferMethods = ({ baseContract, method }: PageProps) => {
return (
<>
{method === 'transferFrom' && <>{method}</>}
{method === 'transferToken' && <>{method}</>}
{method === 'transferTokens' && <>{method}</>}
{method === 'crypto' && <CryptoTransfer baseContract={baseContract} />}
</>
);
};

export default HederaTokenTransferMethods;
Loading

0 comments on commit 3457154

Please sign in to comment.