Skip to content

Commit

Permalink
dapp-feat: TransferToken feature complete (#319) (#382)
Browse files Browse the repository at this point in the history
* dapp-update: added gasLimit to TransferSingleToken API

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

* dapp-update: updated util component for TransferSingleToken

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

* dapp-feat: finished TransferSingleToken feature

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

* dapp-update: merged transferSignleToken and transferSignleTokenFrom into 1 API

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

* dapp-feat: TransferSingleTokenFrom Feature complete (#318)

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

---------

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Sep 11, 2023
1 parent 3457154 commit 95d6f6a
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
transferFungibleTokens,
transferNonFungibleTokens,
transferSingleToken,
transferSingleTokenFrom,
} from '@/api/hedera/tokenTransfer-interactions';
import {
IHederaTokenServiceTokenTransferList,
Expand Down Expand Up @@ -245,7 +244,8 @@ describe('TokenTransferContract test suite', () => {
hederaTokenAddress,
senderA,
receiverA,
369
369,
gasLimit
);

expect(txRes.err).toBeNull;
Expand All @@ -260,7 +260,8 @@ describe('TokenTransferContract test suite', () => {
hederaTokenAddress,
senderA,
receiverA,
369
369,
gasLimit
);

expect(txRes.err).toBeNull;
Expand All @@ -275,7 +276,8 @@ describe('TokenTransferContract test suite', () => {
'0xabc',
senderA,
receiverA,
369
369,
gasLimit
);

expect(txRes.err).toBe('Invalid token address');
Expand All @@ -290,7 +292,8 @@ describe('TokenTransferContract test suite', () => {
hederaTokenAddress,
'0xabc',
receiverA,
369
369,
gasLimit
);

expect(txRes.err).toBe('Invalid sender address');
Expand All @@ -305,7 +308,8 @@ describe('TokenTransferContract test suite', () => {
hederaTokenAddress,
senderA,
'0xabc',
369
369,
gasLimit
);

expect(txRes.err).toBe('Invalid receiver address');
Expand All @@ -320,7 +324,8 @@ describe('TokenTransferContract test suite', () => {
hederaTokenAddress,
senderA,
receiverB,
-369
-369,
gasLimit
);

expect(txRes.err).toBe('Invalid quantity');
Expand All @@ -331,28 +336,30 @@ 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;
expect(txRes.result).toBe(true);
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;
Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SmartContractExecutionResult>
*/
export const transferSingleToken = async (
baseContract: Contract,
API: 'FUNGIBLE' | 'NON_FUNGIBLE',
hederaTokenAddress: string,
sender: string,
receiver: string,
quantity: number
): Promise<SmartContractExecutionResult> => {
// 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
*
Expand All @@ -284,13 +217,14 @@ export const transferSingleToken = async (
*
* @return Promise<SmartContractExecutionResult>
*/
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<SmartContractExecutionResult> => {
// sanitize params
let sanitizeErr;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -85,7 +86,7 @@ const CryptoTransferInputFields = ({
fontWeight={'medium'}
>
<Select
_focus={{ borderColor: '#A98DF4' }}
_focus={{ borderColor: HEDERA_BRANDING_COLORS.purple }}
className="w-[200px] hover:cursor-pointer rounded-md border-white/30"
placeholder="Approval Status"
onChange={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/

import { Dispatch, SetStateAction } from 'react';
import { HEDERA_BRANDING_COLORS } from '@/utils/common/constants';
import { convertCalmelCaseFunctionName } from '@/utils/common/helpers';
import {
Tr,
Expand Down Expand Up @@ -89,7 +90,9 @@ bg-secondary text-white font-styrene w-[30rem]"
<Table variant="simple" size={'sm'}>
<Tbody>
<Tr>
<Th color={'#82ACF9'}>{convertCalmelCaseFunctionName(eventMaps[APIMethods])}</Th>
<Th color={HEDERA_BRANDING_COLORS.violet}>
{convertCalmelCaseFunctionName(eventMaps[APIMethods])}
</Th>
{APIMethods === 'GET_APPROVED' && (
<Td className="flex justify-end">
{`${tokenInfo.slice(0, 6)}...${tokenInfo.slice(-6)}`}
Expand Down
Loading

0 comments on commit 95d6f6a

Please sign in to comment.