0xmetaschool/ Token Multisender

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.

src
Components
App.jsx
index.css
main.jsx
.gitignore
BatchTransfer.sol
eslint.config.js
index.html
LICENSE
MetaToken.sol
package.json
postcss.config.js
README.md
tailwind.config.js
vite.config.js
srcseparatorApp.jsx
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
Downloads

0

Version

1.0.0

License

MIT

Platform

Ethereum, Solidity

Contributors 3
aveek-goyal
jatinmeta
AveekGoyal
Open in

Last updated 3 months ago

We are a open source platform and encourage community to contribute to these templates!