Skip to content

Commit

Permalink
dapp-feat: Remove Current Deployed Contract Instance feature complete (
Browse files Browse the repository at this point in the history
…#415)

* dapp-feat: Remove Current Contract Instance feature complete

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

* dapp-update: removed clearCachedTransactions when remove contract instance

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

* dapp-update: added sessionedContractAddress to TranasctionResult

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

* dapp-update: added new "transactions to show" logic to contract interactions

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

* dapp-feat: cached contract create transactions to localStorage

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

* dapp-update: added a new custom hook to filter transactions by contract address

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

---------

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Sep 21, 2023
1 parent 568fdc7 commit 77e6ade
Show file tree
Hide file tree
Showing 39 changed files with 647 additions and 206 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ContractFactory } from 'ethers';
import { deploySmartContract } from '@/api/hedera';
import { HederaSmartContractResult } from '@/types/common';
import { HEDERA_SMART_CONTRACTS_ASSETS } from '@/utils/common/constants';
import { MOCK_CONTRACT_ID, MOCK_TX_HASH } from '../utils/common/constants';

// Mock ethers
jest.mock('ethers', () => {
Expand Down Expand Up @@ -53,13 +54,15 @@ describe('deploySmartContract', () => {
it('should deploy the smart contract', async () => {
// prepare states
const deployParams = [100];
const contractAddr = '0x8f18eCFeC4dB88ACe84dD1c8d11eBFeDd9274324';
const contractABI = HEDERA_SMART_CONTRACTS_ASSETS.EXCHANGE_RATE.contractABI;
const contractBytecode = HEDERA_SMART_CONTRACTS_ASSETS.EXCHANGE_RATE.contractBytecode;

// mock contractDeployTx
const mockContractDeployTx = {
getAddress: jest.fn().mockResolvedValue(contractAddr),
getAddress: jest.fn().mockResolvedValue(MOCK_CONTRACT_ID),
deploymentTransaction: jest.fn().mockResolvedValue({
hash: MOCK_TX_HASH,
}),
};

// mock contract
Expand All @@ -80,7 +83,7 @@ describe('deploySmartContract', () => {
// validation
expect(result.err).toBeNull;
expect(deploySmartContract).toBeCalled;
expect(result.contractAddress).toEqual(contractAddr);
expect(result.contractAddress).toEqual(MOCK_CONTRACT_ID);
expect(mockContractDeployTx.getAddress).toHaveBeenCalled();
expect(mockContract.deploy).toHaveBeenCalledWith(...deployParams, { gasLimit: 4_000_000 });
});
Expand Down
9 changes: 9 additions & 0 deletions system-contract-dapp-playground/src/api/cookies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ export const getInfoFromCookies = (
}
};

/**
* @dev remove specific cookie
*
* @param key: string
*/
export const removeCookieAt = (key: string) => {
Cookies.remove(key);
};

/**
* @dev clear account information stored in cookies
*/
Expand Down
36 changes: 28 additions & 8 deletions system-contract-dapp-playground/src/api/hedera/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

import { ContractFactory } from 'ethers';
import { getWalletProvider } from '../wallet';
import { TransactionResult } from '@/types/contract-interactions/HTS';
import { ContractABI, HederaSmartContractResult } from '@/types/common';
import { HEDERA_TRANSACTION_RESULT_STORAGE_KEYS } from '@/utils/common/constants';

/**
* @dev deploys smart contract to Hedera network
Expand All @@ -38,13 +40,18 @@ export const deploySmartContract = async (
contractBytecode: string,
params: any[]
): Promise<HederaSmartContractResult> => {
// get wallet provider
// states
const transactionResultStorageKey = HEDERA_TRANSACTION_RESULT_STORAGE_KEYS['CONTRACT-CREATE'];

// get contract create transactions from localStorage
const cachedCreateTransactions = localStorage.getItem(transactionResultStorageKey);
const contractCreateTransactions = cachedCreateTransactions ? JSON.parse(cachedCreateTransactions) : [];

// get signer
const walletProvider = getWalletProvider();
if (walletProvider.err || !walletProvider.walletProvider) {
return { err: walletProvider.err };
}

// get signer
const walletSigner = await walletProvider.walletProvider.getSigner();

// Deploy smart contract
Expand All @@ -53,11 +60,7 @@ export const deploySmartContract = async (
const gasLimit = 4_000_000;

// get contract from contract factory
const contract = new ContractFactory(
JSON.stringify(contractABI),
contractBytecode,
walletSigner
);
const contract = new ContractFactory(JSON.stringify(contractABI), contractBytecode, walletSigner);

// execute deploy transaction
const contractDeployTx = await contract.deploy(...params, {
Expand All @@ -66,6 +69,23 @@ export const deploySmartContract = async (

// get contractAddress
const contractAddress = await contractDeployTx.getAddress();

// retrieve transaction receipt
const txReceipt = contractDeployTx.deploymentTransaction();

// prepare create transaction result
if (txReceipt) {
const createTransactionResult: TransactionResult = {
status: 'success',
transactionTimeStamp: Date.now(),
txHash: txReceipt.hash as string,
transactionType: 'CONTRACT-CREATE',
sessionedContractAddress: contractAddress,
};
contractCreateTransactions.push(createTransactionResult);
localStorage.setItem(transactionResultStorageKey, JSON.stringify(contractCreateTransactions));
}

return { contractAddress };
} catch (err) {
console.error(err);
Expand Down
24 changes: 19 additions & 5 deletions system-contract-dapp-playground/src/api/localStorage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,33 @@ export const getArrayTypedValuesFromLocalStorage = (key: string) => {
};

/**
* @dev clear all HEDERA transaction results cached in localStorage
* @dev clear HEDERA transaction results cached in localStorage
*
* @param contractKey?: string
*
* @param readonly?: boolean
*/
export const clearTransactionCache = () => {
export const clearCachedTransactions = (contractKey?: string, readonly?: boolean) => {
// prepare key
const targetKey = contractKey ? contractKey : OFFCIAL_NETWORK_NAME;

// loop through localStorage items
if (localStorage) {
for (let i = 0; i < localStorage.length; i++) {
// get key
const key = localStorage.key(i);

// remove items that have keys start with HEDERA
if (key?.startsWith(OFFCIAL_NETWORK_NAME)) {
localStorage.removeItem(key);
i--;
if (key?.includes(targetKey)) {
if (readonly) {
if (key?.includes('READONLY')) {
localStorage.removeItem(key);
i--;
}
} else {
localStorage.removeItem(key);
i--;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*-
*
* 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 {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
} from '@chakra-ui/react';

interface PageProps {
isOpen: boolean;
modalBody: any;
onClose: () => void;
modalHeader: string;
handleAcknowledge: any;
}

const ConfirmModal = ({
isOpen,
modalBody,
onClose,
modalHeader,
handleAcknowledge,
}: PageProps) => {
return (
<Modal isOpen={isOpen} isCentered onClose={onClose}>
<ModalOverlay />
<ModalContent
className="h-fit flex flex-col gap-3 rounded-xl drop-shadow-xl
bg-secondary text-white font-styrene w-[30rem]"
>
<ModalHeader>{modalHeader}</ModalHeader>
<ModalCloseButton />

{/* break line */}
<hr className="border-t border-white/40 -mt-3" />

<ModalBody>{modalBody}</ModalBody>

<ModalFooter>
<button
onClick={handleAcknowledge}
className="border border-button-stroke-violet px-6 py-2 rounded-lg font-medium hover:bg-button-stroke-violet hover:text-white transition duration-300"
>
Acknowledge
</button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

export default ConfirmModal;
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
*
*/

import Cookies from 'js-cookie';
import { useToast } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
import { Contract, isAddress } from 'ethers';
import { erc20Mint } from '@/api/hedera/erc20-interactions';
import { CommonErrorToast } from '@/components/toast/CommonToast';
import MultiLineMethod from '@/components/common/MultiLineMethod';
import { TransactionResult } from '@/types/contract-interactions/HTS';
import { HEDERA_TRANSACTION_RESULT_STORAGE_KEYS } from '@/utils/common/constants';
import { mintParamFields } from '@/utils/contract-interactions/erc/erc20/constant';
import { CONTRACT_NAMES, HEDERA_TRANSACTION_RESULT_STORAGE_KEYS } from '@/utils/common/constants';
import { handleAPIErrors } from '@/components/contract-interaction/hts/shared/methods/handleAPIErrors';
import { useUpdateTransactionResultsToLocalStorage } from '@/components/contract-interaction/hts/shared/hooks/useUpdateLocalStorage';

Expand All @@ -38,6 +39,7 @@ const Mint = ({ baseContract }: PageProps) => {
const toaster = useToast();
const [isLoading, setIsLoading] = useState(false);
const [isSuccessful, setIsSuccessful] = useState(false);
const currentContractAddress = Cookies.get(CONTRACT_NAMES.ERC20) as string;
const [transactionResults, setTransactionResults] = useState<TransactionResult[]>([]);
const transactionResultStorageKey = HEDERA_TRANSACTION_RESULT_STORAGE_KEYS['ERC20-RESULT']['TOKEN-MINT'];
const [mintParams, setMintParams] = useState({
Expand Down Expand Up @@ -74,6 +76,7 @@ const Mint = ({ baseContract }: PageProps) => {
setTransactionResults,
transactionHash: txHash,
transactionType: 'ERC20-MINT',
sessionedContractAddress: currentContractAddress,
});
return;
} else {
Expand All @@ -85,6 +88,7 @@ const Mint = ({ baseContract }: PageProps) => {
txHash: txHash as string,
transactionType: 'ERC20-MINT',
transactionTimeStamp: Date.now(),
sessionedContractAddress: currentContractAddress,
},
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*
*/

import Cookies from 'js-cookie';
import { Contract } from 'ethers';
import { isAddress } from 'ethers';
import { BiCopy } from 'react-icons/bi';
Expand All @@ -39,7 +40,14 @@ import {
HEDERA_CHAKRA_INPUT_BOX_SIZES,
HEDERA_COMMON_WALLET_REVERT_REASONS,
HEDERA_TRANSACTION_RESULT_STORAGE_KEYS,
CONTRACT_NAMES,
} from '@/utils/common/constants';
import {
approveParamFields,
allowanceParamFields,
increaseAllowanceParamFields,
decreaseAllowanceParamFields,
} from '@/utils/contract-interactions/erc/erc20/constant';
import {
Td,
Th,
Expand All @@ -54,12 +62,6 @@ import {
PopoverTrigger,
TableContainer,
} from '@chakra-ui/react';
import {
approveParamFields,
allowanceParamFields,
increaseAllowanceParamFields,
decreaseAllowanceParamFields,
} from '@/utils/contract-interactions/erc/erc20/constant';

interface PageProps {
baseContract: Contract;
Expand All @@ -74,6 +76,7 @@ type Allowance = {
const TokenPermission = ({ baseContract }: PageProps) => {
const toaster = useToast();
const [allowances, setAllowances] = useState<Allowance[]>([]);
const currentContractAddress = Cookies.get(CONTRACT_NAMES.ERC20) as string;
const [transactionResults, setTransactionResults] = useState<TransactionResult[]>([]);
const transactionResultStorageKey =
HEDERA_TRANSACTION_RESULT_STORAGE_KEYS['ERC20-RESULT']['TOKEN-PERMISSION'];
Expand Down Expand Up @@ -212,6 +215,7 @@ const TokenPermission = ({ baseContract }: PageProps) => {
setTransactionResults,
err: tokenPermissionRes.err,
transactionHash: tokenPermissionRes.txHash,
sessionedContractAddress: currentContractAddress,
transactionType: (transferTypeMap as any)[method],
});
return;
Expand Down Expand Up @@ -263,6 +267,7 @@ const TokenPermission = ({ baseContract }: PageProps) => {
status: 'success',
transactionTimeStamp: Date.now(),
txHash: tokenPermissionRes.txHash as string,
sessionedContractAddress: currentContractAddress,
transactionType: (transferTypeMap as any)[method],
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*
*/

import Cookies from 'js-cookie';
import { Contract } from 'ethers';
import { isAddress } from 'ethers';
import { useToast } from '@chakra-ui/react';
Expand All @@ -27,7 +28,7 @@ import MultiLineMethod from '@/components/common/MultiLineMethod';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { TransactionResult } from '@/types/contract-interactions/HTS';
import { convertCalmelCaseFunctionName } from '@/utils/common/helpers';
import { HEDERA_TRANSACTION_RESULT_STORAGE_KEYS } from '@/utils/common/constants';
import { CONTRACT_NAMES, HEDERA_TRANSACTION_RESULT_STORAGE_KEYS } from '@/utils/common/constants';
import { handleAPIErrors } from '@/components/contract-interaction/hts/shared/methods/handleAPIErrors';
import { useUpdateTransactionResultsToLocalStorage } from '@/components/contract-interaction/hts/shared/hooks/useUpdateLocalStorage';
import { handleRetrievingTransactionResultsFromLocalStorage } from '@/components/contract-interaction/hts/shared/methods/handleRetrievingTransactionResultsFromLocalStorage';
Expand All @@ -44,6 +45,7 @@ const Transfer = ({ baseContract }: PageProps) => {
const toaster = useToast();
const transactionResultStorageKey =
HEDERA_TRANSACTION_RESULT_STORAGE_KEYS['ERC20-RESULT']['TOKEN-TRANSFER'];
const currentContractAddress = Cookies.get(CONTRACT_NAMES.ERC20) as string;
const [transactionResults, setTransactionResults] = useState<TransactionResult[]>([]);

const [transferParams, setTransferParams] = useState({
Expand Down Expand Up @@ -128,6 +130,7 @@ const Transfer = ({ baseContract }: PageProps) => {
setTransactionResults,
err: tokenTransferRes.err,
transactionHash: tokenTransferRes.txHash,
sessionedContractAddress: currentContractAddress,
transactionType: `ERC20-${convertCalmelCaseFunctionName(method).replace(' ', '-')}`,
});
return;
Expand All @@ -142,6 +145,7 @@ const Transfer = ({ baseContract }: PageProps) => {
status: 'success',
transactionTimeStamp: Date.now(),
txHash: tokenTransferRes.txHash as string,
sessionedContractAddress: currentContractAddress,
transactionType: `ERC20-${convertCalmelCaseFunctionName(method).toUpperCase().replace(' ', '-')}`,
},
]);
Expand Down
Loading

0 comments on commit 77e6ade

Please sign in to comment.