{
"data": {
"fdaomultixender.near": {
"widget": {
"MultiXender_main": {
"": "// Styling components\nconst Mainpage = styled.div`\n background-color: #edf0f4;\n height: auto;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n`;\n\nconst Text_style = styled.div`\n font-family: 'Lato', sans-serif;\n color: #072754;\n margin-top: 100px;\n font-weight: bold;\n display: flex;\n text-align: center;\n justify-content: center;\n font-size: 3rem;\n @media screen and (max-width: 500px) {\n display: flex;\n font-size: 2rem;\n }\n`;\n\nconst Second_p = styled.div`\n font-family: 'Lato', sans-serif;\n display: flex;\n justify-content: center;\n color: #072754;\n font-size: 2rem;\n margin-top: 10px;\n text-align: center;\n @media screen and (max-width: 500px) {\n font-size: 1.5rem;\n display: flex;\n margin: 10px auto;\n justify-content: center;\n text-align: center;\n }\n`;\n\nconst Third_p = styled.p`\n color: #072754;\n font-family: 'Lato', sans-serif;\n font-weight: bold;\n margin-top: 50px;\n display: flex;\n justify-content: center;\n font-size: 2.5rem;\n\n span {\n color: #ff0000; /* Red color for the 'X' */\n }\n`;\n\nconst NextUpper = styled.button`\n font-family: 'Lato', sans-serif;\n height: 60px;\n width: 200px;\n margin-top: 20px;\n border: none;\n border-radius: 30px;\n background-color: #f8931f;\n color: #000;\n font-weight: bold;\n font-size: 18px;\n cursor: pointer;\n position: relative;\n overflow: hidden;\n transition: all 0.3s ease;\n\n &:before {\n content: \"\";\n position: absolute;\n top: 50%;\n left: 50%;\n width: 300%;\n height: 300%;\n background-color: rgba(255, 255, 255, 0.1);\n transition: all 0.3s ease;\n border-radius: 50%;\n z-index: 0;\n transform: translate(-50%, -50%);\n }\n\n &:hover:before {\n width: 0;\n height: 0;\n }\n\n &:hover {\n color: #000;\n background-color: #df841c;\n }\n\n &:active {\n transform: scale(0.95);\n }\n\n &:focus {\n outline: none;\n }\n @media screen and (max-width: 500px) {\n width: 150px;\n font-size: 16px;\n }\n`;\n\nconst NextLower = styled.button`\n font-family: 'Lato', sans-serif;\n height: 60px;\n width: 200px;\n margin-top: 20px;\n margin-right: 10px;\n border: none;\n border-radius: 30px;\n background-color: #f8931f;\n color: #000;\n font-weight: bold;\n font-size: 18px;\n cursor: pointer;\n position: relative;\n overflow: hidden;\n transition: all 0.3s ease;\n\n &:before {\n content: \"\";\n position: absolute;\n top: 50%;\n left: 50%;\n width: 300%;\n height: 300%;\n background-color: rgba(255, 255, 255, 0.1);\n transition: all 0.3s ease;\n border-radius: 50%;\n z-index: 0;\n transform: translate(-50%, -50%);\n }\n\n &:hover:before {\n width: 0;\n height: 0;\n }\n\n &:hover {\n color: #000;\n background-color: #df841c;\n }\n\n &:active {\n transform: scale(0.95);\n }\n\n &:focus {\n outline: none;\n }\n\n @media screen and (max-width: 500px) {\n width: 150px;\n font-size: 16px;\n }\n`;\n\nconst SimplePopup = styled.div`\n position: fixed;\n top: 85%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: white;\n padding: 8px;\n border-radius: 10px;\n z-index: 1000;\n @media screen and (max-width: 500px) {\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n`;\n\nconst InputLabel = styled.label`\n font-weight: bold;\n display: flex;\n justify-content: space-between;\n color: white;\n align-items: center;\n font-size: 1.5rem;\n margin-bottom: 5px;\n\n @media screen and (max-width: 500px) {\n display: none;\n }\n`;\n\nconst Label1 = styled.label`\n display: flex;\n justify-content: center;\n font-family: 'Lato', sans-serif;\n color: #072754;\n font-size: 1.5rem;\n margin: 20px;\n\n @media screen and (max-width: 500px) {\n margin-bottom: 5px;\n margin-left: 0%;\n text-align: center;\n }\n`;\n\nconst Label2 = styled.label`\n display: flex;\n color: #072754;\n font-family: 'Lato', sans-serif;\n font-size: 1.5rem;\n margin-right: 40px;\n align-items: center;\n justify-content: center;\n @media screen and (max-width: 500px) {\n margin-bottom: 5px;\n margin-left: 0%;\n text-align: center;\n }\n`;\n\nconst CloseButton = styled.button`\n margin-top: 5px;\n padding: 5px;\n border: 1px solid #ccc;\n border-radius: 5px;\n background: #f1f1f1;\n cursor: pointer;\n margin: auto;\n display: flex;\n justify-content: center;\n`;\n\nconst Container = styled.div`\n margin-top: 100px;\n`;\n\nconst Inputdiv = styled.div`\n height: auto;\n display: flex;\n justify-content: center;\n align-items: center;\n flex-direction: column;\n\n @media screen and (max-width: 500px) {\n margin-bottom: 5px;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n margin-top: -100px;\n }\n`;\n\nconst Plus = styled.div`\n background-color: white;\n color: black;\n padding: 10px;\n width: 50%;\n border-radius: 8px;\n margin-left: 23%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n border: 2px solid transparent;\n transition: background-color 0.3s, border-color 0.3s;\n\n &:hover {\n background-color: #072754;\n border-color: #072754;\n }\n .plus-icon {\n color: red;\n }\n @media screen and (max-width: 500px) {\n margin: auto;\n display: flex;\n justify-content: center;\n }\n`;\n\nconst Divbutton = styled.div`\n display: flex;\n width: 100%;\n margin-top: 20px;\n justify-content: center;\n`;\n\nconst DeleteButton = styled.button`\n width: 50px;\n display: inline-block;\n transition: width 0.3s ease;\n\n &:hover {\n width: 50px;\n transform: scale(1.1);\n }\n\n @media screen and (max-width: 500px) {\n margin: auto;\n display: flex;\n justify-content: center;\n }\n`;\n\nconst InputAddress = styled.input`\n height: 40px;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #ccc;\n font-size: 16px;\n border-radius: 10px;\n @media screen and (max-width: 500px) {\n margin: auto;\n display: flex;\n justify-content: center;\n }\n`;\n\nconst InputAmount = styled.input`\n height: 40px;\n padding: 8px;\n border-radius: 4px;\n border: 1px solid #ccc;\n font-size: 16px;\n border-radius: 10px;\n width: 100px;\n @media screen and (max-width: 500px) {\n margin: auto;\n display: flex;\n justify-content: center;\n }\n`;\n\nconst FooterContainer = styled.div`\n height: 80px;\n margin-top: 200px;\n color: #000;\n background-color: #edf0f4;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: Arial, sans-serif;\n\n p {\n font-size: 1.5rem;\n margin: 0;\n }\n\n @media screen and (max-width: 500px) {\n height: 120px;\n margin-top: 100px;\n padding: 20px;\n text-align: center;\n p {\n font-size: 1.1rem;\n }\n }\n`;\n\nconst Footer = () => {\n return (\n <FooterContainer>\n <p>© 2024 Multixender All rights reserved.</p>\n </FooterContainer>\n );\n};\n\nconst Text = () => (\n <Text_style>\n <p>Send Tokens to Multiple Addresses</p>\n </Text_style>\n);\n\nconst Text2 = () => (\n <Second_p>\n <p>Empowered by NEAR Protocol's cutting-edge technology</p>\n </Second_p>\n);\n\nconst Text3 = () => (\n <Third_p>\n Multi<span>X</span>ender\n </Third_p>\n);\n\n// Function to get config\nfunction getConfig(network) {\n switch (network) {\n case \"mainnet\":\n return {\n nodeUrl: \"https://rpc.mainnet.near.org\",\n };\n case \"testnet\":\n return {\n nodeUrl: \"https://rpc.testnet.near.org\",\n };\n default:\n throw Error(`Unconfigured environment '${network}'.`);\n }\n}\n\nconst config = getConfig(\"mainnet\");\nconst usdtConfig = \"usdt.tether-token.near\";\n\n// Main component working\nconst Main = () => {\n const [isSimplePopupVisible, setSimplePopupVisibility] = useState(false);\n const [addressList, setAddressList] = useState([]);\n const [totalAmount, setTotalAmount] = useState(0);\n const [popupContent, setPopupContent] = useState(\"\");\n const [distributeInput, setDistributeInput] = useState(\"\");\n const [isTransferCompleted, setTransferCompleted] = useState(false);\n const [selectedToken, setSelectedToken] = useState(\"NEAR\");\n\n const handleCloseSimplePopup = () => {\n setSimplePopupVisibility(false);\n };\n\n const handleStoreList = () => {\n const newList = handleSubmit();\n\n const addressSet = new Set();\n const hasDuplicate = newList.some((item) => {\n if (addressSet.has(item.address)) {\n console.log(`Duplicate address found: ${item.address}`);\n setPopupContent(\"Repeated accounts were found.\");\n setSimplePopupVisibility(true);\n return true;\n }\n addressSet.add(item.address);\n return false;\n });\n\n if (hasDuplicate || newList.length === 0) {\n return {};\n }\n\n const sumOfAmounts = Object.values(newList).reduce(\n (total, item) => total + parseFloat(item.amount || 0),\n 0\n );\n\n if (sumOfAmounts < 1) {\n setPopupContent(\"Sum of amount must be more than 1 NEAR.\");\n setSimplePopupVisibility(true);\n return {};\n } else {\n let platformAmount = sumOfAmounts * 0.005 + newList.length * 0.004;\n const calculatedTotalAmount = sumOfAmounts + platformAmount;\n\n setPopupContent(\n `Total Amount: ${calculatedTotalAmount}\\nPlatform Amount: ${platformAmount}\\nGas Fees: 0.0001`\n );\n\n const recipients = Object.values(newList).map((item) => ({\n account_id: item.address,\n amount: Number(\n (selectedToken === \"NEAR\"\n ? item.amount * 1e24\n : selectedToken === \"USDT\"\n ? item.amount * 1e6\n : item.amount\n ).toString()\n )\n .toLocaleString(\"fullwide\", { useGrouping: false })\n .toString(),\n }));\n\n setSimplePopupVisibility(true);\n return {\n calculatedTotalAmount,\n distributeInput: {\n input: JSON.stringify({ recipients }),\n },\n platformAmount,\n sumOfAmounts,\n };\n }\n };\n\n const handleSubmit = () => {\n const newList = inputs;\n return newList;\n };\n const handleftmethod = () => {\n for (let index = 0; index < inputs.length; index++) {\n if (state[`validAccount_${index}`] === false) {\n setPopupContent(\"Enter valid Account id\");\n setSimplePopupVisibility(true);\n return;\n } else if (inputs[index].address.trim() === \"\") {\n setPopupContent(\"Account id should not be blank\");\n setSimplePopupVisibility(true);\n return;\n }\n }\n const {\n calculatedTotalAmount,\n distributeInput,\n platformAmount,\n sumOfAmounts,\n } = handleStoreList();\n\n if (!calculatedTotalAmount || !distributeInput || distributeInput === \"\") {\n State.update({\n disp: true,\n });\n\n return \"\";\n }\n\n setSimplePopupVisibility(false);\n let newsumOfAmounts =\n selectedToken === \"USDT\" ? sumOfAmounts * 1e6 : sumOfAmounts;\n try {\n Near.call([\n {\n contractName: \"usdt.fdaomultixender.near\",\n methodName: \"set_recipients\",\n args: distributeInput,\n deposit:\n selectedToken === \"USDT\"\n ? platformAmount * 1e23\n : platformAmount * 1e17,\n gas: 100000000000000,\n },\n {\n contractName:\n selectedToken === \"USDT\"\n ? \"usdt.tether-token.near\"\n : \"4e807467ba9e3119d5356c5568ef63e9c321b471.factory.bridge.near\",\n methodName: \"ft_transfer_call\",\n args: {\n receiver_id: \"usdt.fdaomultixender.near\",\n amount: `${newsumOfAmounts}`,\n msg: \"Sending Near to Recepients\",\n },\n deposit: 1,\n gas: 100000000000000,\n },\n ]);\n } catch (error) {\n console.error(\"Error during Near.call:\", error);\n }\n };\n const handleMethod = () => {\n for (let index = 0; index < inputs.length; index++) {\n if (state[`validAccount_${index}`] === false) {\n setPopupContent(\"Enter valid Account id\");\n setSimplePopupVisibility(true);\n return;\n } else if (inputs[index].address.trim() === \"\") {\n setPopupContent(\"Account id should not be blank\");\n setSimplePopupVisibility(true);\n return;\n }\n }\n const {\n calculatedTotalAmount,\n distributeInput,\n platformAmount,\n sumOfAmounts,\n } = handleStoreList();\n\n if (!calculatedTotalAmount || !distributeInput || distributeInput === \"\") {\n State.update({\n disp: true,\n });\n\n return \"\";\n }\n\n setSimplePopupVisibility(false);\n\n const Contract = \"multi.fdaomultixender.near\";\n const Method = \"transfer\";\n const transferAccountId = \"\";\n const Gas = 100000000000000;\n const newcalculatedTotalAmount = calculatedTotalAmount * 1e24;\n\n try {\n Near.call(\n Contract,\n Method,\n distributeInput,\n transferAccountId,\n newcalculatedTotalAmount\n );\n } catch (error) {\n console.error(\"Error during Near.call:\", error);\n }\n };\n\n const getAddressList = () => {\n if (typeof textAreaContent === \"string\") {\n const jsonArray = textAreaContent\n .split(\"\\n\")\n .filter((line) => line.trim() !== \"\")\n .map((line, index) => {\n const [address, amount] = line.split(\",\").map((item) => item.trim());\n return {\n key: index + 1,\n address,\n amount: parseFloat(amount) || 0,\n };\n });\n\n return jsonArray;\n } else {\n return [];\n }\n };\n\n useEffect(() => {\n setAddressList(getAddressList());\n }, [textAreaContent]);\n\n const [inputs, setInputs] = useState([\n { key: 1, address: \"\", amount: \"\", usdAmount: \"\" },\n ]);\n\n const handleAdd = () => {\n setInputs((prevInputs) => [\n ...prevInputs,\n {\n key: prevInputs.length + 1,\n address: \"\",\n amount: \"\",\n usdAmount: \"\",\n },\n ]);\n };\n\n const handleDelete = () => {\n if (inputs.length > 1) {\n setInputs((prevInputs) => prevInputs.slice(0, -1));\n }\n };\n\n const handleDeleteInput = (index) => {\n setInputs((prevInputs) => prevInputs.filter((input, i) => i !== index));\n };\n\n const handleInputChange = (event, index, field) => {\n const { value } = event.target;\n let updatedInputs = [...inputs];\n let input = updatedInputs[index];\n\n if (field === \"address\") {\n input.address = value;\n setInputs(updatedInputs);\n return;\n }\n\n if (value === \"\") {\n input[field] = \"\";\n if (field === \"amount\") {\n input.usdAmount = \"\";\n } else if (field === \"usdAmount\") {\n input.amount = \"\";\n }\n setInputs(updatedInputs);\n return;\n }\n\n if (!value.match(/^\\d*\\.?\\d*$/)) {\n setPopupContent(\"Field must consist of numbers only.\");\n setSimplePopupVisibility(true);\n return;\n }\n\n const numValue = parseFloat(value);\n if (numValue < 0) {\n setPopupContent(\"The field can only be positive.\");\n setSimplePopupVisibility(true);\n return;\n }\n if (numValue === 0) {\n setPopupContent(\"The field cannot be zero.\");\n setSimplePopupVisibility(true);\n return;\n }\n\n if (field === \"amount\") {\n input.amount = value;\n input.usdAmount = (numValue * res).toFixed(2);\n } else if (field === \"usdAmount\") {\n input.usdAmount = value;\n input.amount = (numValue / res).toFixed(2);\n }\n\n setInputs(updatedInputs);\n };\n\n const initialValidationStates = inputs.map(() => undefined);\n const [validationStates, setValidationStates] = useState(\n initialValidationStates\n );\n\n const updateValidationState = (index, isValid) => {\n setValidationStates((prevStates) => {\n const newValidationStates = [...prevStates];\n newValidationStates[index] = isValid;\n return newValidationStates;\n });\n };\n\n const data = fetch(\"https://api.coingecko.com/api/v3/coins/near\", {\n subscribe: true,\n method: \"GET\",\n headers: {\n Accept: \"*/*\",\n },\n });\n\n const main_price = data.body.market_data.current_price.usd;\n\n const res = main_price;\n\n return (\n <Container>\n <div\n style={{\n display: \"flex\",\n width: \"100%\",\n justifyContent: \"left\",\n marginBottom: \"10px\",\n }}\n >\n <Label1>Token Address</Label1>\n <select\n value={selectedToken}\n onChange={(e) => setSelectedToken(e.target.value)}\n style={{\n width: \"50%\",\n fontSize: \"1.2rem\",\n height: \"34px\",\n marginTop: \"20px\",\n }}\n >\n <option value=\"NEAR\">NEAR</option>\n <option value=\"USDT\">USDT</option>\n <option value=\"OTTO\">OTTO</option>\n </select>\n </div>\n <InputLabel>\n <Label1>Account Id</Label1>\n <Label2>Token Amount</Label2>\n </InputLabel>\n <Inputdiv>\n {inputs.map((input, index) => (\n <div key={input.key} className=\"flex justify-between mb-2\">\n {state[`validAccount_${index}`] === false && (\n <div\n style={{\n color: \"red\",\n display: \"flex\",\n justifyContent: \"space-between\",\n }}\n >\n Account does not exist\n </div>\n )}\n <InputAddress\n value={input.address}\n name=\"address\"\n placeholder=\"Enter account ID\"\n style={{\n border:\n state[`validAccount_${index}`] === false\n ? \"2px solid red\"\n : state[`validAccount_${index}`]\n ? \"2px solid green\"\n : \"none\",\n }}\n onChange={(event) => {\n handleInputChange(event, index, \"address\");\n\n const value = event.target.value;\n\n clearTimeout(state.timer);\n State.update({\n timer: setTimeout(() => {\n if (value !== \"\") {\n asyncFetch(config.nodeUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: \"dontcare\",\n method: \"query\",\n params: {\n request_type: \"view_account\",\n finality: \"final\",\n account_id: value,\n },\n }),\n }).then((response) => {\n if (response.body.error) {\n State.update({ [`validAccount_${index}`]: false });\n } else {\n State.update({ [`validAccount_${index}`]: true });\n }\n });\n } else {\n State.update({\n [`validAccount_${index}`]: undefined,\n });\n }\n }, 300),\n });\n }}\n />\n <InputAmount\n type=\"text\"\n className=\"m-2 p-2 w-1/2 amount\"\n placeholder={\n selectedToken === \"NEAR\"\n ? \"NEAR\"\n : selectedToken === \"USDT\"\n ? \"USDT\"\n : \"OTTO\"\n }\n name=\"Amount\"\n value={input.amount}\n onChange={(event) => handleInputChange(event, index, \"amount\")}\n style={{ textAlign: \"centre\" }}\n />\n <InputAmount\n type=\"text\"\n className=\"m-2 p-2 w-1/2 amount\"\n placeholder=\"USD\"\n name=\"Amount\"\n value={\n selectedToken === \"NEAR\"\n ? input.usdAmount\n : selectedToken === \"USDT\"\n ? input.amount\n : \"NA\"\n }\n onChange={(event) => handleInputChange(event, index, \"usdAmount\")}\n style={{ textAlign: \"centre\" }}\n />\n\n <DeleteButton onClick={() => handleDeleteInput(index)}>\n ❌\n </DeleteButton>\n </div>\n ))}\n </Inputdiv>\n\n <Plus onClick={handleAdd}>\n <span role=\"img\" aria-label=\"Add Icon\" className=\"plus-icon\">\n ➕\n </span>\n </Plus>\n\n <Divbutton>\n <NextUpper onClick={handleStoreList} style={{ marginRight: \"20px\" }}>\n Show fees\n </NextUpper>\n <NextLower\n onClick={selectedToken === \"NEAR\" ? handleMethod : handleftmethod}\n >\n Multisend\n </NextLower>\n </Divbutton>\n {isSimplePopupVisible && (\n <SimplePopup>\n <div>{popupContent}</div>\n <CloseButton onClick={handleCloseSimplePopup}>❌</CloseButton>\n </SimplePopup>\n )}\n {isSubmitClicked && <Content3 distributeInput={distributeInput} />}\n </Container>\n );\n};\n\nreturn (\n <Mainpage>\n <Text />\n <Text2 />\n <Text3 />\n <Main />\n <Footer />\n </Mainpage>\n);\n",
"metadata": {
"fork_of": "fdaomultixender.near/widget/MultiXender_main@121929964"
}
}
}
}
}
}