diff --git a/minting-dapp/package.json b/minting-dapp/package.json index 2fd7e7f60..f241ad0c6 100644 --- a/minting-dapp/package.json +++ b/minting-dapp/package.json @@ -36,5 +36,8 @@ "dev": "encore dev", "watch": "encore dev --watch", "build": "encore production --progress" + }, + "dependencies": { + "react-toastify": "^9.0.3" } } diff --git a/minting-dapp/src/scripts/main.tsx b/minting-dapp/src/scripts/main.tsx index af2ea8f51..2ac2acc23 100644 --- a/minting-dapp/src/scripts/main.tsx +++ b/minting-dapp/src/scripts/main.tsx @@ -1,13 +1,23 @@ import '../styles/main.scss'; +import 'react-toastify/dist/ReactToastify.css'; import ReactDOM from 'react-dom'; import Dapp from './react/Dapp'; import CollectionConfig from '../../../smart-contract/config/CollectionConfig'; +import { ToastContainer } from 'react-toastify'; if (document.title === '') { document.title = CollectionConfig.tokenName; } document.addEventListener('DOMContentLoaded', async () => { - ReactDOM.render(, document.getElementById('minting-dapp')); + ReactDOM.render(<> + + + , document.getElementById('minting-dapp')); }); diff --git a/minting-dapp/src/scripts/react/Dapp.tsx b/minting-dapp/src/scripts/react/Dapp.tsx index a9db7b664..8e69dfa9d 100644 --- a/minting-dapp/src/scripts/react/Dapp.tsx +++ b/minting-dapp/src/scripts/react/Dapp.tsx @@ -8,6 +8,7 @@ import NetworkConfigInterface from '../../../../smart-contract/lib/NetworkConfig import CollectionStatus from './CollectionStatus'; import MintWidget from './MintWidget'; import Whitelist from '../lib/Whitelist'; +import { toast } from 'react-toastify'; const ContractAbi = require('../../../../smart-contract/artifacts/contracts/' + CollectionConfig.contractName + '.sol/' + CollectionConfig.contractName + '.json').abi; @@ -23,6 +24,7 @@ interface State { maxMintAmountPerTx: number; tokenPrice: BigNumber; isPaused: boolean; + loading: boolean; isWhitelistMintEnabled: boolean; isUserInWhitelist: boolean; merkleProofManualAddress: string; @@ -39,6 +41,7 @@ const defaultState: State = { maxMintAmountPerTx: 0, tokenPrice: BigNumber.from(0), isPaused: true, + loading: false, isWhitelistMintEnabled: false, isUserInWhitelist: false, merkleProofManualAddress: '', @@ -63,7 +66,7 @@ export default class Dapp extends React.Component { const browserProvider = await detectEthereumProvider() as ExternalProvider; if (browserProvider?.isMetaMask !== true) { - this.setError( + this.setError( <> We were not able to detect MetaMask. We value privacy and security a lot so we limit the wallet options on the DAPP.

@@ -84,16 +87,44 @@ export default class Dapp extends React.Component { async mintTokens(amount: number): Promise { try { - await this.contract.mint(amount, {value: this.state.tokenPrice.mul(amount)}); + this.setState({loading: true}); + const transaction = await this.contract.mint(amount, {value: this.state.tokenPrice.mul(amount)}); + + toast.info(<> + Transaction sent! Waiting...
+ View on {this.state.networkConfig.blockExplorer.name} + ); + + const receipt = await transaction.wait(); + + toast.success(<> + Success!
+ View on {this.state.networkConfig.blockExplorer.name} + ); + + this.setState({loading: false}); } catch (e) { this.setError(e); + this.setState({loading: false}); } } async whitelistMintTokens(amount: number): Promise { try { - await this.contract.whitelistMint(amount, Whitelist.getProofForAddress(this.state.userAddress!), {value: this.state.tokenPrice.mul(amount)}); + const transaction = await this.contract.whitelistMint(amount, Whitelist.getProofForAddress(this.state.userAddress!), {value: this.state.tokenPrice.mul(amount)}); + + toast.info(<> + Transaction sent! Waiting...
+

{transaction.hash}

+ ); + + const receipt = await transaction.wait(); + + toast.success(<> + Success!
+

{receipt.transactionHash}

+ ); } catch (e) { this.setError(e); } @@ -134,7 +165,7 @@ export default class Dapp extends React.Component { navigator.clipboard.writeText(merkleProof); this.setState({ - merkleProofManualAddressFeedbackMessage: + merkleProofManualAddressFeedbackMessage: <> Congratulations! 🎉
Your Merkle Proof has been copied to the clipboard. You can paste it into {this.state.networkConfig.blockExplorer.name} to claim your tokens. @@ -153,7 +184,7 @@ export default class Dapp extends React.Component { : null} {this.state.errorMessage ?

{this.state.errorMessage}

: null} - + {this.isWalletConnected() ? <> {this.isContractReady() ? @@ -178,6 +209,7 @@ export default class Dapp extends React.Component { isUserInWhitelist={this.state.isUserInWhitelist} mintTokens={(mintAmount) => this.mintTokens(mintAmount)} whitelistMintTokens={(mintAmount) => this.whitelistMintTokens(mintAmount)} + loading={this.state.loading} /> :
@@ -203,7 +235,7 @@ export default class Dapp extends React.Component { {!this.isWalletConnected() || !this.isSoldOut() ?
{!this.isWalletConnected() ? : null} - +
Hey, looking for a super-safe experience? 😃
You can interact with the smart-contract directly through {this.state.networkConfig.blockExplorer.name}, without even connecting your wallet to this DAPP! 🚀
@@ -246,7 +278,7 @@ export default class Dapp extends React.Component { errorMessage = error.message; } else if (React.isValidElement(error)) { this.setState({errorMessage: error}); - + return; } } @@ -266,6 +298,11 @@ export default class Dapp extends React.Component { return CollectionConfig.marketplaceConfig.generateCollectionUrl(CollectionConfig.marketplaceIdentifier, !this.isNotMainnet()); } + private generateTransactionUrl(transactionHash: string): string + { + return this.state.networkConfig.blockExplorer.generateTransactionUrl(transactionHash); + } + private async connectWallet(): Promise { try { @@ -280,7 +317,7 @@ export default class Dapp extends React.Component { private async initWallet(): Promise { const walletAccounts = await this.provider.listAccounts(); - + this.setState(defaultState); if (walletAccounts.length === 0) { @@ -299,7 +336,7 @@ export default class Dapp extends React.Component { return; } - + this.setState({ userAddress: walletAccounts[0], network, diff --git a/minting-dapp/src/scripts/react/MintWidget.tsx b/minting-dapp/src/scripts/react/MintWidget.tsx index a3503f09e..d4b259070 100644 --- a/minting-dapp/src/scripts/react/MintWidget.tsx +++ b/minting-dapp/src/scripts/react/MintWidget.tsx @@ -9,6 +9,7 @@ interface Props { tokenPrice: BigNumber; maxMintAmountPerTx: number; isPaused: boolean; + loading: boolean; isWhitelistMintEnabled: boolean; isUserInWhitelist: boolean; mintTokens(mintAmount: number): Promise; @@ -64,7 +65,7 @@ export default class MintWidget extends React.Component { return ( <> {this.canMint() ? -
+
Collection preview
@@ -74,16 +75,16 @@ export default class MintWidget extends React.Component {
- + {this.state.mintAmount} - - + +
:
- + {this.props.isWhitelistMintEnabled ? <>You are not included in the whitelist. : <>The contract is paused.}
Please come back during the next sale!
diff --git a/minting-dapp/yarn.lock b/minting-dapp/yarn.lock index e9b8e115b..1b73ff585 100644 --- a/minting-dapp/yarn.lock +++ b/minting-dapp/yarn.lock @@ -2712,6 +2712,11 @@ clone@^2.0.0, clone@^2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= +clsx@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" + integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -6116,6 +6121,13 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-toastify@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.0.3.tgz#8e6d22651c85cb584c5ebd0b5e2c3bf0d7ec06ee" + integrity sha512-0QZJk0SqYBxouRBGCFU3ymvjlwimRRhVH7SzqGRiVrQ001KSoUNbGKx9Yq42aoPv18n45yJzEFG82zqv3HnASg== + dependencies: + clsx "^1.1.1" + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" diff --git a/smart-contract/lib/NetworkConfigInterface.ts b/smart-contract/lib/NetworkConfigInterface.ts index 6d03b6892..b2b3f3840 100644 --- a/smart-contract/lib/NetworkConfigInterface.ts +++ b/smart-contract/lib/NetworkConfigInterface.ts @@ -4,5 +4,6 @@ export default interface NetworkConfigInterface { blockExplorer: { name: string; generateContractUrl: (contractAddress: string) => string; + generateTransactionUrl: (transactionAddress: string) => string; }; }; diff --git a/smart-contract/lib/Networks.ts b/smart-contract/lib/Networks.ts index 502a7e7cc..7d963b2aa 100644 --- a/smart-contract/lib/Networks.ts +++ b/smart-contract/lib/Networks.ts @@ -9,6 +9,7 @@ export const hardhatLocal: NetworkConfigInterface = { blockExplorer: { name: 'Block explorer (not available for local chains)', generateContractUrl: (contractAddress: string) => `#`, + generateTransactionUrl: (transactionAddress: string) => `#`, }, } @@ -21,6 +22,7 @@ export const ethereumTestnet: NetworkConfigInterface = { blockExplorer: { name: 'Etherscan (Rinkeby)', generateContractUrl: (contractAddress: string) => `https://rinkeby.etherscan.io/address/${contractAddress}`, + generateTransactionUrl: (transactionAddress: string) => `https://rinkeby.etherscan.io/tx/${transactionAddress}`, }, } @@ -30,6 +32,7 @@ export const ethereumMainnet: NetworkConfigInterface = { blockExplorer: { name: 'Etherscan', generateContractUrl: (contractAddress: string) => `https://etherscan.io/address/${contractAddress}`, + generateTransactionUrl: (transactionAddress: string) => `https://etherscan.io/tx/${transactionAddress}`, }, } @@ -42,6 +45,7 @@ export const polygonTestnet: NetworkConfigInterface = { blockExplorer: { name: 'Polygonscan (Mumbai)', generateContractUrl: (contractAddress: string) => `https://mumbai.polygonscan.com/address/${contractAddress}`, + generateTransactionUrl: (transactionAddress: string) => `https://mumbai.polygonscan.com/tx/${transactionAddress}`, }, } @@ -51,5 +55,6 @@ export const polygonMainnet: NetworkConfigInterface = { blockExplorer: { name: 'Polygonscan', generateContractUrl: (contractAddress: string) => `https://polygonscan.com/address/${contractAddress}`, + generateTransactionUrl: (transactionAddress: string) => `https://polygonscan.com/tx/${transactionAddress}`, }, }