Token MultiSender is a decentralized application enabling users to send ERC-20 tokens to multiple addresses in a single transaction, optimizing gas costs and simplifying bulk transfers. Built with React.js and styled with Tailwind CSS, this application allow user to send tokens to multiple addresses in a single transaction, saving time and reducing gas fees.
1import { useState, useEffect } from "react";
2import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
3import Step1 from "./Components/Step1/Step1";
4import Step2 from "./Components/Step2/Step2";
5import Step3 from "./Components/Step3/Step3";
6import Footer from "./Components/Footer";
7import Navbar from './Components/Navbar';
8import LandingPage from './Components/LandingPage';
9import { ethers } from "ethers";
10
11const MultiSenderApp = () => {
12 const [currentStep, setCurrentStep] = useState(1);
13 const [sharedState, setSharedState] = useState({
14 walletAddress: "",
15 addresses: [],
16 amounts: [],
17 selectedToken: "META",
18 customTokenAddress: "",
19 totalAmount: "0",
20 estimatedGas: null,
21 receipt: null,
22 txnHash: null,
23 errorMessage: "",
24 custom_token_balance:"0",
25 custom_token_symbol:"ERC20",
26 token_balance:"0",
27 provider: null,
28 signer: null,
29 contract: null,
30 ethBalance: null,
31 CONTRACT_ADDRESS:"0x41c108bba45ffc0ceee17a1dabaddd738bd3ab43",
32 approved_i:false,
33 });
34
35 const [set_faucet_txn_hash] = useState(null);
36 const [isLoading, setIsLoading] = useState(false);
37 const [isFaucetModalOpen, setFaucetModalOpen] = useState(false);
38
39 const handleFaucetClick = () => {
40 setFaucetModalOpen(true);
41 };
42
43 const closeFaucetModal = () => {
44 setFaucetModalOpen(false);
45 }
46
47
48
49 useEffect(() => {
50 balance_func();
51 checkAndSwitchToSepolia();
52 }, [isFaucetModalOpen]);
53
54
55let balance_func =async()=>
56{
57 const contract = new ethers.Contract("0xd312f1C56bfe9be58a36C4747a945FC699a9C079" , [
58 "function balanceOf(address) view returns (uint256)"
59 ], sharedState.signer);
60
61 let balance = await contract.balanceOf(sharedState.walletAddress);// Update the MetaToken balance after minting
62 setSharedState((prevState) => ({...prevState,token_balance: ethers.utils.formatUnits(balance, 18)}));
63}
64
65
66 const mintMetaToken = async () => {
67 try
68 {
69 setIsLoading(true);
70 const contract = new ethers.Contract("0xd312f1C56bfe9be58a36C4747a945FC699a9C079" , [
71 "function faucet() external",
72 ], sharedState.signer);
73
74 let tx =await contract.faucet( { gasLimit: 210000 }); // Call the faucet function
75 await tx.wait(); // Wait for the transaction to be mined
76 set_faucet_txn_hash(tx.hash);
77 setFaucetModalOpen(false); // Close the modal after minting
78
79 }
80 catch (error)
81 {
82 console.error("Error minting tokens:", error);
83 }
84 finally
85 {setIsLoading(false);}
86 };
87
88 const updateSharedState = (updates) => {
89 setSharedState(prevState => ({
90 ...prevState,
91 ...updates
92 }));
93 };
94
95 const handleNext = () => {
96 if (currentStep < 3) {
97 setCurrentStep(currentStep + 1);
98 }
99 };
100
101 const handleBack = () => {
102 if (currentStep > 1) {
103 setCurrentStep(currentStep - 1);
104 }
105 };
106
107
108 const checkAndSwitchToSepolia = async () => {
109 try {
110 const { ethereum } = window;
111 if (!ethereum) {
112 console.error("Metamask is not installed.");
113 return;
114 }
115
116 const provider = new ethers.providers.Web3Provider(ethereum);
117 const network = await provider.getNetwork();
118
119 // Check if the network is Sepolia (chainId: 11155111)
120 if (network.chainId !== 11155111) {
121 try {
122 await ethereum.request({
123 method: "wallet_switchEthereumChain",
124 params: [{ chainId: "0xAA36A7" }], // Hexadecimal for 11155111
125 });
126 } catch (error) {
127 // If the user doesn't have Sepolia, try adding it
128 if (error.code === 4902) {
129 try {
130 await ethereum.request({
131 method: "wallet_addEthereumChain",
132 params: [
133 {
134 chainId: "0xAA36A7",
135 chainName: "Sepolia Testnet",
136 nativeCurrency: {
137 name: "Sepolia ETH",
138 symbol: "ETH",
139 decimals: 18,
140 },
141 rpcUrls: ["https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID"],
142 blockExplorerUrls: ["https://sepolia.etherscan.io"],
143 },
144 ],
145 });
146 } catch (addError) {
147 console.error("Error adding Sepolia network:", addError);
148 }
149 } else {
150 console.error("Error switching network:", error);
151 }
152 }
153 }
154 } catch (error) {
155 console.error("Error checking or switching network:", error);
156 }
157 };
158
159
160
161
162 return (
163 <div className="min-h-screen flex flex-col bg-black text-gray-100 font-['Source_Sans_Pro']">
164 <Navbar
165 onFaucetClick={handleFaucetClick}
166 showFaucet={sharedState.walletAddress && currentStep === 1}
167 />
168 <main className="flex-grow flex flex-col items-center justify-start">
169 <div className="w-full max-w-4xl mx-auto px-4">
170 <div className="flex justify-center mb-8 gap-16">
171 {[1, 2, 3].map((step) => (
172 <div
173 key={step}
174 className={`flex items-center ${
175 currentStep === step ? "text-yellow-400" : "text-gray-400"
176 } font-bold text-lg animate-bounceIn`}
177 >
178 <div
179 className={`${
180 currentStep === step
181 ? "bg-yellow-400 text-gray-800"
182 : "bg-gray-700 text-gray-200"
183 } w-[35px] h-[35px] rounded-full flex items-center justify-center mr-3 text-base font-bold animate-scaleUp`}
184 >
185 {step}
186 </div>
187 Step {step}
188 </div>
189 ))}
190 </div>
191
192 <div className="max-w-2xl mx-auto">
193 <div className="bg-gray-800/80 backdrop-blur-sm border-2 border-yellow-400 p-8 rounded-xl shadow-xl animate-slideUp">
194 {currentStep === 1 && (
195 <Step1
196 sharedState={sharedState}
197 updateSharedState={updateSharedState}
198 onNext={handleNext}
199 />
200 )}
201 {currentStep === 2 && (
202 <Step2
203 sharedState={sharedState}
204 updateSharedState={updateSharedState}
205 onNext={handleNext}
206 onBack={handleBack}
207 />
208 )}
209 {currentStep === 3 && (
210 <Step3
211 sharedState={sharedState}
212 updateSharedState={updateSharedState}
213 onBack={handleBack}
214 />
215 )}
216 </div>
217 </div>
218 </div>
219
220 {isFaucetModalOpen && (
221 <div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50">
222 <div className="bg-gray-800 p-8 rounded-xl border border-yellow-400/30 max-w-md w-full mx-4 shadow-2xl">
223 <h2 className="text-2xl font-bold mb-4 text-yellow-400">META Token Faucet</h2>
224 <p className="mb-6 text-gray-300">
225 Click the button below to receive META tokens for testing.
226 </p>
227 <div className="flex justify-end space-x-4">
228 <button
229 onClick={closeFaucetModal}
230 className="px-4 py-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-colors"
231 >
232 Close
233 </button>
234 <button
235 onClick={mintMetaToken}
236 disabled={isLoading}
237 className="px-4 py-2 bg-yellow-400 text-black rounded-lg hover:bg-yellow-500 disabled:bg-gray-400 transition-all duration-300 shadow-lg hover:shadow-yellow-400/30"
238 >
239 {isLoading ? "Minting..." : "Get Tokens"}
240 </button>
241 </div>
242 </div>
243 </div>
244 )}
245 </main>
246
247 <Footer />
248 </div>
249 );
250};
251
252const App = () => {
253 return (
254 <Router>
255 <Routes>
256 <Route path="/" element={<LandingPage />} />
257 <Route path="/app" element={<MultiSenderApp />} />
258 <Route path="*" element={<Navigate to="/" />} />
259 </Routes>
260 </Router>
261 );
262};
263
264export default App;
265
Last updated 3 months ago