├── .gitignore ├── index.html ├── package.json ├── postcss.config.js ├── src ├── App.tsx ├── assets │ ├── arrow.svg │ ├── back.svg │ ├── discord.svg │ ├── github.svg │ ├── logo.svg │ ├── nodata.png │ ├── nodata.svg │ ├── telegram.svg │ └── wechat.jpeg ├── components │ ├── Footer │ │ └── index.tsx │ ├── Header │ │ └── index.tsx │ └── Web3ReactManager │ │ └── index.tsx ├── config │ ├── abi │ │ ├── MutilTransfer.json │ │ ├── erc20.json │ │ └── types │ │ │ ├── Erc20.ts │ │ │ ├── MutilTransfer.ts │ │ │ ├── common.ts │ │ │ ├── factories │ │ │ ├── Erc20__factory.ts │ │ │ ├── MutilTransfer__factory.ts │ │ │ └── index.ts │ │ │ └── index.ts │ ├── connectors │ │ ├── NetworkConnector.ts │ │ └── index.ts │ ├── constants │ │ ├── chainIcon.ts │ │ ├── chainId.ts │ │ ├── contractAddresses.ts │ │ ├── defaultChainId.ts │ │ ├── native.ts │ │ ├── rpc.ts │ │ ├── types.ts │ │ └── wallets.ts │ ├── index.ts │ └── tokens │ │ ├── bsc.json │ │ ├── bsctestnet.json │ │ ├── index.ts │ │ ├── kovan.json │ │ ├── rinkeby.json │ │ └── ropsten.json ├── contracts │ └── MutilTransfer.sol ├── favicon.svg ├── hooks │ ├── useActiveWeb3React.ts │ ├── useContract.ts │ ├── useDebounce.ts │ ├── useEagerConnect.ts │ ├── useInactiveListener.ts │ └── useTransfer.ts ├── index.css ├── logo.svg ├── main.tsx ├── utils │ ├── contractAddressHelper.ts │ ├── format.ts │ ├── index.ts │ ├── isAddress.ts │ └── isEth.ts ├── view │ └── Home │ │ ├── AddressList.tsx │ │ ├── ConfirmPage.tsx │ │ ├── SelectToken.tsx │ │ └── index.tsx └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 批量转账 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-react-app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite --port 8080", 7 | "build": "tsc && vite build", 8 | "preview": "vite preview", 9 | "typechain": "typechain --out-dir src/config/abi/types --target=ethers-v5 \"src/config/abi/*.json\"" 10 | }, 11 | "dependencies": { 12 | "@emotion/react": "^11.10.0", 13 | "@emotion/styled": "^11.10.0", 14 | "@ethersproject/address": "^5.6.1", 15 | "@ethersproject/constants": "^5.6.1", 16 | "@ethersproject/contracts": "^5.6.2", 17 | "@ethersproject/providers": "^5.6.8", 18 | "@mui/lab": "^5.0.0-alpha.94", 19 | "@mui/material": "^5.10.0", 20 | "@typechain/ethers-v5": "^10.1.0", 21 | "@web3-react/abstract-connector": "^6.0.7", 22 | "@web3-react/core": "^6.1.9", 23 | "@web3-react/injected-connector": "^6.0.7", 24 | "bignumber.js": "^9.1.0", 25 | "ethers": "^5.6.9", 26 | "events": "^3.3.0", 27 | "react": "^17.0.2", 28 | "react-dom": "^17.0.2", 29 | "react-router-dom": "5", 30 | "react-toastify": "^9.0.5", 31 | "typechain": "^8.1.0", 32 | "web3modal": "^1.9.8" 33 | }, 34 | "devDependencies": { 35 | "@types/node": "^18.0.1", 36 | "@types/react": "^17.0.20", 37 | "@types/react-dom": "^17.0.9", 38 | "@types/react-router-dom": "^5.3.3", 39 | "@vitejs/plugin-react": "^1.3.0", 40 | "autoprefixer": "^10.4.7", 41 | "postcss": "^8.4.14", 42 | "tailwindcss": "^3.1.4", 43 | "typescript": "^4.6.3", 44 | "vite": "^2.9.9" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "@/components/Footer/index"; 2 | import Web3ReactManager from "@/components/Web3ReactManager/index"; 3 | import { NetworkContextName } from "@/config"; 4 | import { Web3Provider } from "@ethersproject/providers"; 5 | import { createWeb3ReactRoot, Web3ReactProvider } from "@web3-react/core"; 6 | import { HashRouter as Router, Route } from "react-router-dom"; 7 | 8 | import Header from "@/components/Header/index"; 9 | import Home from "@/view/Home"; 10 | export function getLibrary(provider: any): Web3Provider { 11 | const library = new Web3Provider(provider); 12 | library.pollingInterval = 15000; 13 | return library; 14 | } 15 | const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName); 16 | 17 | function App() { 18 | return ( 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | ); 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /src/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/nodata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xverin/MultiTransfer/754c3157841aa83c71422d06da153cc2d5891b08/src/assets/nodata.png -------------------------------------------------------------------------------- /src/assets/nodata.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/telegram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/wechat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xverin/MultiTransfer/754c3157841aa83c71422d06da153cc2d5891b08/src/assets/wechat.jpeg -------------------------------------------------------------------------------- /src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import discord from "@/assets/discord.svg"; 2 | import github from "@/assets/github.svg"; 3 | import telegram from "@/assets/telegram.svg"; 4 | import wechatQR from "@/assets/wechat.jpeg"; 5 | 6 | export default function Footer() { 7 | return ( 8 |
9 |
10 |
11 |
12 | 13 |
微信(欢迎咨询定制Dapp开发)
14 |
15 | {/*
16 |
{ 19 | window.open("https://t.me/daqingchong", "_blank"); 20 | }} 21 | > 22 | https://t.me/daqingchong 23 |
24 |
电报
25 |
*/} 26 |
27 |
28 |
相关链接(欢迎大家来github点一个star)
29 |
30 | github { 35 | window.open("https://github.com/Verin1005", "_blank"); 36 | }} 37 | /> 38 | telegram { 43 | window.open("https://t.me/daqingchong", "_blank"); 44 | }} 45 | /> 46 | discord { 51 | window.open("https://discord.com/users/973583783422988288", "_blank"); 52 | }} 53 | /> 54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import arrow from "@/assets/arrow.svg"; 2 | import logo from "@/assets/logo.svg"; 3 | import { connectorLocalStorageKey } from "@/config/connectors/index"; 4 | import { NETWORK_LABEL } from "@/config/constants/chainIcon"; 5 | import { injected } from "@/config/constants/wallets"; 6 | import { useActiveWeb3React } from "@/hooks/useActiveWeb3React"; 7 | import { formatAddress } from "@/utils/format"; 8 | import Button from "@mui/material/Button"; 9 | import Menu from "@mui/material/Menu"; 10 | import MenuItem from "@mui/material/MenuItem"; 11 | import { UnsupportedChainIdError } from "@web3-react/core"; 12 | import { useState } from "react"; 13 | import { toast, ToastContainer } from "react-toastify"; 14 | import "react-toastify/dist/ReactToastify.css"; 15 | const contractlist = [ 16 | { 17 | chain: "Rinkeby", 18 | link: "https://rinkeby.etherscan.io/address/0xB304F14dc0fF9bC596D5d1f0e4D67dCA3278f8cC", 19 | address: "0xB304F14dc0fF9bC596D5d1f0e4D67dCA3278f8cC", 20 | }, 21 | { 22 | chain: "Kovan", 23 | link: "https://kovan.etherscan.io/address/0xDffd2226496D02a1108618aeA58F9aA3D5A3538F", 24 | address: "0xDffd2226496D02a1108618aeA58F9aA3D5A3538F", 25 | }, 26 | { 27 | chain: "Goerli", 28 | link: "https://goerli.etherscan.io/address/0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 29 | address: "0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 30 | }, 31 | { 32 | chain: "BSC", 33 | link: "https://bscscan.com/address/0xEcC118Ce3bCC08e574010100ac7d5eD65160fc70", 34 | address: "0xEcC118Ce3bCC08e574010100ac7d5eD65160fc70", 35 | }, 36 | { 37 | chain: "BSC Testnet", 38 | link: "https://testnet.bscscan.com/address/0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 39 | address: "0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 40 | }, 41 | ]; 42 | export default function Header() { 43 | const { account, activate, chainId, deactivate, active } = useActiveWeb3React(); 44 | const [anchorEl, setAnchorEl] = useState(null); 45 | const open = Boolean(anchorEl); 46 | const handleClick = (event: React.MouseEvent) => { 47 | setAnchorEl(event.currentTarget); 48 | }; 49 | 50 | const handleClose = () => { 51 | setAnchorEl(null); 52 | }; 53 | 54 | return ( 55 |
56 |
57 |
58 | 批量转账 59 | 74 | 75 | 84 | {contractlist.map((item, index) => ( 85 | { 87 | window.open(item.link, "_blank"); 88 | handleClose(); 89 | }} 90 | key={index} 91 | > 92 |
93 | {item.chain}—{item.address} 94 |
95 |
96 | ))} 97 |
98 |
99 |
100 | {account && ( 101 |
102 |
103 | {NETWORK_LABEL[chainId]} 104 |
105 | {/* erc20转账 */} 111 |
112 | )} 113 | 114 |
{ 117 | activate(injected, undefined, true) 118 | .then(() => { 119 | localStorage.setItem(connectorLocalStorageKey, "injected"); 120 | }) 121 | .catch((error) => { 122 | console.log(error); 123 | 124 | if (error instanceof UnsupportedChainIdError) { 125 | toast.error("Unsupported ChainId", { 126 | position: toast.POSITION.TOP_LEFT, 127 | theme: "colored", 128 | }); 129 | } 130 | }); 131 | }} 132 | > 133 | {account ? formatAddress(account) : "Connect"} 134 | 135 | 136 | {account ? "..." + account.slice(-4) : "Connect"} 137 | 138 | 139 | {account && ( 140 | { 143 | deactivate(); 144 | }} 145 | > 146 | 断开 147 | 148 | )} 149 |
150 |
151 |
152 | 153 | 154 |
155 | ); 156 | } 157 | -------------------------------------------------------------------------------- /src/components/Web3ReactManager/index.tsx: -------------------------------------------------------------------------------- 1 | import { network } from "@/config/constants/wallets"; 2 | import { NetworkContextName } from "@/config/index"; 3 | import { useWeb3React } from "@web3-react/core"; 4 | import { useEffect, useState } from "react"; 5 | 6 | import useEagerConnect from "@/hooks/useEagerConnect"; 7 | import useInactiveListener from "@/hooks/useInactiveListener"; 8 | 9 | export default function Web3ReactManager({ children }: { children: JSX.Element }) { 10 | const { active } = useWeb3React(); 11 | const { active: networkActive, error: networkError, activate: activateNetwork } = useWeb3React(NetworkContextName); 12 | 13 | // try to eagerly connect to an injected provider, if it exists and has granted access already 14 | const triedEager = useEagerConnect(); 15 | 16 | // after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd 17 | useEffect(() => { 18 | if (triedEager && !networkActive && !networkError && !active) { 19 | activateNetwork(network); 20 | } 21 | }, [triedEager, networkActive, networkError, activateNetwork, active]); 22 | 23 | // when there's no account connected, react to logins (broadly speaking) on the injected provider, if it exists 24 | useInactiveListener(!triedEager); 25 | 26 | // handle delayed loader state 27 | const [showLoader, setShowLoader] = useState(false); 28 | useEffect(() => { 29 | const timeout = setTimeout(() => { 30 | setShowLoader(true); 31 | }, 600); 32 | 33 | return () => { 34 | clearTimeout(timeout); 35 | }; 36 | }, []); 37 | 38 | // on page load, do nothing until we've tried to connect to the injected connector 39 | if (!triedEager) { 40 | return null; 41 | } 42 | 43 | // if the account context isn't active, and there's an error on the network context, it's an irrecoverable error 44 | if (!active && networkError) { 45 | return
unknownError
; 46 | } 47 | 48 | // if neither context is active, spin 49 | if (!active && !networkActive) { 50 | return showLoader ?
Loader
: null; 51 | } 52 | 53 | return children; 54 | } 55 | -------------------------------------------------------------------------------- /src/config/abi/MutilTransfer.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, 6 | { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } 7 | ], 8 | "name": "OwnershipTransferred", 9 | "type": "event" 10 | }, 11 | { 12 | "inputs": [], 13 | "name": "fee", 14 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 15 | "stateMutability": "view", 16 | "type": "function" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "owner", 21 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 22 | "stateMutability": "view", 23 | "type": "function" 24 | }, 25 | { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, 26 | { 27 | "inputs": [{ "internalType": "uint256", "name": "_fee", "type": "uint256" }], 28 | "name": "setFee", 29 | "outputs": [], 30 | "stateMutability": "nonpayable", 31 | "type": "function" 32 | }, 33 | { 34 | "inputs": [ 35 | { "internalType": "address payable[]", "name": "to", "type": "address[]" }, 36 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 37 | ], 38 | "name": "transferETH", 39 | "outputs": [], 40 | "stateMutability": "payable", 41 | "type": "function" 42 | }, 43 | { 44 | "inputs": [ 45 | { "internalType": "address payable[]", "name": "to", "type": "address[]" }, 46 | { "internalType": "uint256[]", "name": "amount", "type": "uint256[]" } 47 | ], 48 | "name": "transferMultiETH", 49 | "outputs": [], 50 | "stateMutability": "payable", 51 | "type": "function" 52 | }, 53 | { 54 | "inputs": [ 55 | { "internalType": "address", "name": "_token", "type": "address" }, 56 | { "internalType": "address[]", "name": "to", "type": "address[]" }, 57 | { "internalType": "uint256[]", "name": "amount", "type": "uint256[]" } 58 | ], 59 | "name": "transferMultiToken", 60 | "outputs": [], 61 | "stateMutability": "payable", 62 | "type": "function" 63 | }, 64 | { 65 | "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], 66 | "name": "transferOwnership", 67 | "outputs": [], 68 | "stateMutability": "nonpayable", 69 | "type": "function" 70 | }, 71 | { 72 | "inputs": [ 73 | { "internalType": "address", "name": "_token", "type": "address" }, 74 | { "internalType": "address[]", "name": "to", "type": "address[]" }, 75 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 76 | ], 77 | "name": "transferToken", 78 | "outputs": [], 79 | "stateMutability": "payable", 80 | "type": "function" 81 | } 82 | ] 83 | -------------------------------------------------------------------------------- /src/config/abi/erc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "_name", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "_symbol", 12 | "type": "string" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "constructor" 18 | }, 19 | { 20 | "anonymous": false, 21 | "inputs": [ 22 | { 23 | "indexed": true, 24 | "internalType": "address", 25 | "name": "owner", 26 | "type": "address" 27 | }, 28 | { 29 | "indexed": true, 30 | "internalType": "address", 31 | "name": "spender", 32 | "type": "address" 33 | }, 34 | { 35 | "indexed": false, 36 | "internalType": "uint256", 37 | "name": "value", 38 | "type": "uint256" 39 | } 40 | ], 41 | "name": "Approval", 42 | "type": "event" 43 | }, 44 | { 45 | "anonymous": false, 46 | "inputs": [ 47 | { 48 | "indexed": true, 49 | "internalType": "address", 50 | "name": "from", 51 | "type": "address" 52 | }, 53 | { 54 | "indexed": true, 55 | "internalType": "address", 56 | "name": "to", 57 | "type": "address" 58 | }, 59 | { 60 | "indexed": false, 61 | "internalType": "uint256", 62 | "name": "value", 63 | "type": "uint256" 64 | } 65 | ], 66 | "name": "Transfer", 67 | "type": "event" 68 | }, 69 | { 70 | "constant": true, 71 | "inputs": [ 72 | { 73 | "internalType": "address", 74 | "name": "owner", 75 | "type": "address" 76 | }, 77 | { 78 | "internalType": "address", 79 | "name": "spender", 80 | "type": "address" 81 | } 82 | ], 83 | "name": "allowance", 84 | "outputs": [ 85 | { 86 | "internalType": "uint256", 87 | "name": "", 88 | "type": "uint256" 89 | } 90 | ], 91 | "payable": false, 92 | "stateMutability": "view", 93 | "type": "function" 94 | }, 95 | { 96 | "constant": false, 97 | "inputs": [ 98 | { 99 | "internalType": "address", 100 | "name": "spender", 101 | "type": "address" 102 | }, 103 | { 104 | "internalType": "uint256", 105 | "name": "amount", 106 | "type": "uint256" 107 | } 108 | ], 109 | "name": "approve", 110 | "outputs": [ 111 | { 112 | "internalType": "bool", 113 | "name": "", 114 | "type": "bool" 115 | } 116 | ], 117 | "payable": false, 118 | "stateMutability": "nonpayable", 119 | "type": "function" 120 | }, 121 | { 122 | "constant": true, 123 | "inputs": [ 124 | { 125 | "internalType": "address", 126 | "name": "account", 127 | "type": "address" 128 | } 129 | ], 130 | "name": "balanceOf", 131 | "outputs": [ 132 | { 133 | "internalType": "uint256", 134 | "name": "", 135 | "type": "uint256" 136 | } 137 | ], 138 | "payable": false, 139 | "stateMutability": "view", 140 | "type": "function" 141 | }, 142 | { 143 | "constant": true, 144 | "inputs": [], 145 | "name": "decimals", 146 | "outputs": [ 147 | { 148 | "internalType": "uint256", 149 | "name": "", 150 | "type": "uint256" 151 | } 152 | ], 153 | "payable": false, 154 | "stateMutability": "view", 155 | "type": "function" 156 | }, 157 | { 158 | "constant": false, 159 | "inputs": [ 160 | { 161 | "internalType": "address", 162 | "name": "spender", 163 | "type": "address" 164 | }, 165 | { 166 | "internalType": "uint256", 167 | "name": "subtractedValue", 168 | "type": "uint256" 169 | } 170 | ], 171 | "name": "decreaseAllowance", 172 | "outputs": [ 173 | { 174 | "internalType": "bool", 175 | "name": "", 176 | "type": "bool" 177 | } 178 | ], 179 | "payable": false, 180 | "stateMutability": "nonpayable", 181 | "type": "function" 182 | }, 183 | { 184 | "constant": false, 185 | "inputs": [ 186 | { 187 | "internalType": "address", 188 | "name": "spender", 189 | "type": "address" 190 | }, 191 | { 192 | "internalType": "uint256", 193 | "name": "addedValue", 194 | "type": "uint256" 195 | } 196 | ], 197 | "name": "increaseAllowance", 198 | "outputs": [ 199 | { 200 | "internalType": "bool", 201 | "name": "", 202 | "type": "bool" 203 | } 204 | ], 205 | "payable": false, 206 | "stateMutability": "nonpayable", 207 | "type": "function" 208 | }, 209 | { 210 | "constant": true, 211 | "inputs": [], 212 | "name": "name", 213 | "outputs": [ 214 | { 215 | "internalType": "string", 216 | "name": "", 217 | "type": "string" 218 | } 219 | ], 220 | "payable": false, 221 | "stateMutability": "view", 222 | "type": "function" 223 | }, 224 | { 225 | "constant": true, 226 | "inputs": [], 227 | "name": "symbol", 228 | "outputs": [ 229 | { 230 | "internalType": "string", 231 | "name": "", 232 | "type": "string" 233 | } 234 | ], 235 | "payable": false, 236 | "stateMutability": "view", 237 | "type": "function" 238 | }, 239 | { 240 | "constant": true, 241 | "inputs": [], 242 | "name": "totalSupply", 243 | "outputs": [ 244 | { 245 | "internalType": "uint256", 246 | "name": "", 247 | "type": "uint256" 248 | } 249 | ], 250 | "payable": false, 251 | "stateMutability": "view", 252 | "type": "function" 253 | }, 254 | { 255 | "constant": false, 256 | "inputs": [ 257 | { 258 | "internalType": "address", 259 | "name": "recipient", 260 | "type": "address" 261 | }, 262 | { 263 | "internalType": "uint256", 264 | "name": "amount", 265 | "type": "uint256" 266 | } 267 | ], 268 | "name": "transfer", 269 | "outputs": [ 270 | { 271 | "internalType": "bool", 272 | "name": "", 273 | "type": "bool" 274 | } 275 | ], 276 | "payable": false, 277 | "stateMutability": "nonpayable", 278 | "type": "function" 279 | }, 280 | { 281 | "constant": false, 282 | "inputs": [ 283 | { 284 | "internalType": "address", 285 | "name": "sender", 286 | "type": "address" 287 | }, 288 | { 289 | "internalType": "address", 290 | "name": "recipient", 291 | "type": "address" 292 | }, 293 | { 294 | "internalType": "uint256", 295 | "name": "amount", 296 | "type": "uint256" 297 | } 298 | ], 299 | "name": "transferFrom", 300 | "outputs": [ 301 | { 302 | "internalType": "bool", 303 | "name": "", 304 | "type": "bool" 305 | } 306 | ], 307 | "payable": false, 308 | "stateMutability": "nonpayable", 309 | "type": "function" 310 | } 311 | ] 312 | -------------------------------------------------------------------------------- /src/config/abi/types/Erc20.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BigNumberish, 8 | BytesLike, 9 | CallOverrides, 10 | ContractTransaction, 11 | Overrides, 12 | PopulatedTransaction, 13 | Signer, 14 | utils, 15 | } from "ethers"; 16 | import type { 17 | FunctionFragment, 18 | Result, 19 | EventFragment, 20 | } from "@ethersproject/abi"; 21 | import type { Listener, Provider } from "@ethersproject/providers"; 22 | import type { 23 | TypedEventFilter, 24 | TypedEvent, 25 | TypedListener, 26 | OnEvent, 27 | PromiseOrValue, 28 | } from "./common"; 29 | 30 | export interface Erc20Interface extends utils.Interface { 31 | functions: { 32 | "allowance(address,address)": FunctionFragment; 33 | "approve(address,uint256)": FunctionFragment; 34 | "balanceOf(address)": FunctionFragment; 35 | "decimals()": FunctionFragment; 36 | "decreaseAllowance(address,uint256)": FunctionFragment; 37 | "increaseAllowance(address,uint256)": FunctionFragment; 38 | "name()": FunctionFragment; 39 | "symbol()": FunctionFragment; 40 | "totalSupply()": FunctionFragment; 41 | "transfer(address,uint256)": FunctionFragment; 42 | "transferFrom(address,address,uint256)": FunctionFragment; 43 | }; 44 | 45 | getFunction( 46 | nameOrSignatureOrTopic: 47 | | "allowance" 48 | | "approve" 49 | | "balanceOf" 50 | | "decimals" 51 | | "decreaseAllowance" 52 | | "increaseAllowance" 53 | | "name" 54 | | "symbol" 55 | | "totalSupply" 56 | | "transfer" 57 | | "transferFrom" 58 | ): FunctionFragment; 59 | 60 | encodeFunctionData( 61 | functionFragment: "allowance", 62 | values: [PromiseOrValue, PromiseOrValue] 63 | ): string; 64 | encodeFunctionData( 65 | functionFragment: "approve", 66 | values: [PromiseOrValue, PromiseOrValue] 67 | ): string; 68 | encodeFunctionData( 69 | functionFragment: "balanceOf", 70 | values: [PromiseOrValue] 71 | ): string; 72 | encodeFunctionData(functionFragment: "decimals", values?: undefined): string; 73 | encodeFunctionData( 74 | functionFragment: "decreaseAllowance", 75 | values: [PromiseOrValue, PromiseOrValue] 76 | ): string; 77 | encodeFunctionData( 78 | functionFragment: "increaseAllowance", 79 | values: [PromiseOrValue, PromiseOrValue] 80 | ): string; 81 | encodeFunctionData(functionFragment: "name", values?: undefined): string; 82 | encodeFunctionData(functionFragment: "symbol", values?: undefined): string; 83 | encodeFunctionData( 84 | functionFragment: "totalSupply", 85 | values?: undefined 86 | ): string; 87 | encodeFunctionData( 88 | functionFragment: "transfer", 89 | values: [PromiseOrValue, PromiseOrValue] 90 | ): string; 91 | encodeFunctionData( 92 | functionFragment: "transferFrom", 93 | values: [ 94 | PromiseOrValue, 95 | PromiseOrValue, 96 | PromiseOrValue 97 | ] 98 | ): string; 99 | 100 | decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result; 101 | decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result; 102 | decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result; 103 | decodeFunctionResult(functionFragment: "decimals", data: BytesLike): Result; 104 | decodeFunctionResult( 105 | functionFragment: "decreaseAllowance", 106 | data: BytesLike 107 | ): Result; 108 | decodeFunctionResult( 109 | functionFragment: "increaseAllowance", 110 | data: BytesLike 111 | ): Result; 112 | decodeFunctionResult(functionFragment: "name", data: BytesLike): Result; 113 | decodeFunctionResult(functionFragment: "symbol", data: BytesLike): Result; 114 | decodeFunctionResult( 115 | functionFragment: "totalSupply", 116 | data: BytesLike 117 | ): Result; 118 | decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result; 119 | decodeFunctionResult( 120 | functionFragment: "transferFrom", 121 | data: BytesLike 122 | ): Result; 123 | 124 | events: { 125 | "Approval(address,address,uint256)": EventFragment; 126 | "Transfer(address,address,uint256)": EventFragment; 127 | }; 128 | 129 | getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment; 130 | getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment; 131 | } 132 | 133 | export interface ApprovalEventObject { 134 | owner: string; 135 | spender: string; 136 | value: BigNumber; 137 | } 138 | export type ApprovalEvent = TypedEvent< 139 | [string, string, BigNumber], 140 | ApprovalEventObject 141 | >; 142 | 143 | export type ApprovalEventFilter = TypedEventFilter; 144 | 145 | export interface TransferEventObject { 146 | from: string; 147 | to: string; 148 | value: BigNumber; 149 | } 150 | export type TransferEvent = TypedEvent< 151 | [string, string, BigNumber], 152 | TransferEventObject 153 | >; 154 | 155 | export type TransferEventFilter = TypedEventFilter; 156 | 157 | export interface Erc20 extends BaseContract { 158 | connect(signerOrProvider: Signer | Provider | string): this; 159 | attach(addressOrName: string): this; 160 | deployed(): Promise; 161 | 162 | interface: Erc20Interface; 163 | 164 | queryFilter( 165 | event: TypedEventFilter, 166 | fromBlockOrBlockhash?: string | number | undefined, 167 | toBlock?: string | number | undefined 168 | ): Promise>; 169 | 170 | listeners( 171 | eventFilter?: TypedEventFilter 172 | ): Array>; 173 | listeners(eventName?: string): Array; 174 | removeAllListeners( 175 | eventFilter: TypedEventFilter 176 | ): this; 177 | removeAllListeners(eventName?: string): this; 178 | off: OnEvent; 179 | on: OnEvent; 180 | once: OnEvent; 181 | removeListener: OnEvent; 182 | 183 | functions: { 184 | allowance( 185 | owner: PromiseOrValue, 186 | spender: PromiseOrValue, 187 | overrides?: CallOverrides 188 | ): Promise<[BigNumber]>; 189 | 190 | approve( 191 | spender: PromiseOrValue, 192 | amount: PromiseOrValue, 193 | overrides?: Overrides & { from?: PromiseOrValue } 194 | ): Promise; 195 | 196 | balanceOf( 197 | account: PromiseOrValue, 198 | overrides?: CallOverrides 199 | ): Promise<[BigNumber]>; 200 | 201 | decimals(overrides?: CallOverrides): Promise<[BigNumber]>; 202 | 203 | decreaseAllowance( 204 | spender: PromiseOrValue, 205 | subtractedValue: PromiseOrValue, 206 | overrides?: Overrides & { from?: PromiseOrValue } 207 | ): Promise; 208 | 209 | increaseAllowance( 210 | spender: PromiseOrValue, 211 | addedValue: PromiseOrValue, 212 | overrides?: Overrides & { from?: PromiseOrValue } 213 | ): Promise; 214 | 215 | name(overrides?: CallOverrides): Promise<[string]>; 216 | 217 | symbol(overrides?: CallOverrides): Promise<[string]>; 218 | 219 | totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>; 220 | 221 | transfer( 222 | recipient: PromiseOrValue, 223 | amount: PromiseOrValue, 224 | overrides?: Overrides & { from?: PromiseOrValue } 225 | ): Promise; 226 | 227 | transferFrom( 228 | sender: PromiseOrValue, 229 | recipient: PromiseOrValue, 230 | amount: PromiseOrValue, 231 | overrides?: Overrides & { from?: PromiseOrValue } 232 | ): Promise; 233 | }; 234 | 235 | allowance( 236 | owner: PromiseOrValue, 237 | spender: PromiseOrValue, 238 | overrides?: CallOverrides 239 | ): Promise; 240 | 241 | approve( 242 | spender: PromiseOrValue, 243 | amount: PromiseOrValue, 244 | overrides?: Overrides & { from?: PromiseOrValue } 245 | ): Promise; 246 | 247 | balanceOf( 248 | account: PromiseOrValue, 249 | overrides?: CallOverrides 250 | ): Promise; 251 | 252 | decimals(overrides?: CallOverrides): Promise; 253 | 254 | decreaseAllowance( 255 | spender: PromiseOrValue, 256 | subtractedValue: PromiseOrValue, 257 | overrides?: Overrides & { from?: PromiseOrValue } 258 | ): Promise; 259 | 260 | increaseAllowance( 261 | spender: PromiseOrValue, 262 | addedValue: PromiseOrValue, 263 | overrides?: Overrides & { from?: PromiseOrValue } 264 | ): Promise; 265 | 266 | name(overrides?: CallOverrides): Promise; 267 | 268 | symbol(overrides?: CallOverrides): Promise; 269 | 270 | totalSupply(overrides?: CallOverrides): Promise; 271 | 272 | transfer( 273 | recipient: PromiseOrValue, 274 | amount: PromiseOrValue, 275 | overrides?: Overrides & { from?: PromiseOrValue } 276 | ): Promise; 277 | 278 | transferFrom( 279 | sender: PromiseOrValue, 280 | recipient: PromiseOrValue, 281 | amount: PromiseOrValue, 282 | overrides?: Overrides & { from?: PromiseOrValue } 283 | ): Promise; 284 | 285 | callStatic: { 286 | allowance( 287 | owner: PromiseOrValue, 288 | spender: PromiseOrValue, 289 | overrides?: CallOverrides 290 | ): Promise; 291 | 292 | approve( 293 | spender: PromiseOrValue, 294 | amount: PromiseOrValue, 295 | overrides?: CallOverrides 296 | ): Promise; 297 | 298 | balanceOf( 299 | account: PromiseOrValue, 300 | overrides?: CallOverrides 301 | ): Promise; 302 | 303 | decimals(overrides?: CallOverrides): Promise; 304 | 305 | decreaseAllowance( 306 | spender: PromiseOrValue, 307 | subtractedValue: PromiseOrValue, 308 | overrides?: CallOverrides 309 | ): Promise; 310 | 311 | increaseAllowance( 312 | spender: PromiseOrValue, 313 | addedValue: PromiseOrValue, 314 | overrides?: CallOverrides 315 | ): Promise; 316 | 317 | name(overrides?: CallOverrides): Promise; 318 | 319 | symbol(overrides?: CallOverrides): Promise; 320 | 321 | totalSupply(overrides?: CallOverrides): Promise; 322 | 323 | transfer( 324 | recipient: PromiseOrValue, 325 | amount: PromiseOrValue, 326 | overrides?: CallOverrides 327 | ): Promise; 328 | 329 | transferFrom( 330 | sender: PromiseOrValue, 331 | recipient: PromiseOrValue, 332 | amount: PromiseOrValue, 333 | overrides?: CallOverrides 334 | ): Promise; 335 | }; 336 | 337 | filters: { 338 | "Approval(address,address,uint256)"( 339 | owner?: PromiseOrValue | null, 340 | spender?: PromiseOrValue | null, 341 | value?: null 342 | ): ApprovalEventFilter; 343 | Approval( 344 | owner?: PromiseOrValue | null, 345 | spender?: PromiseOrValue | null, 346 | value?: null 347 | ): ApprovalEventFilter; 348 | 349 | "Transfer(address,address,uint256)"( 350 | from?: PromiseOrValue | null, 351 | to?: PromiseOrValue | null, 352 | value?: null 353 | ): TransferEventFilter; 354 | Transfer( 355 | from?: PromiseOrValue | null, 356 | to?: PromiseOrValue | null, 357 | value?: null 358 | ): TransferEventFilter; 359 | }; 360 | 361 | estimateGas: { 362 | allowance( 363 | owner: PromiseOrValue, 364 | spender: PromiseOrValue, 365 | overrides?: CallOverrides 366 | ): Promise; 367 | 368 | approve( 369 | spender: PromiseOrValue, 370 | amount: PromiseOrValue, 371 | overrides?: Overrides & { from?: PromiseOrValue } 372 | ): Promise; 373 | 374 | balanceOf( 375 | account: PromiseOrValue, 376 | overrides?: CallOverrides 377 | ): Promise; 378 | 379 | decimals(overrides?: CallOverrides): Promise; 380 | 381 | decreaseAllowance( 382 | spender: PromiseOrValue, 383 | subtractedValue: PromiseOrValue, 384 | overrides?: Overrides & { from?: PromiseOrValue } 385 | ): Promise; 386 | 387 | increaseAllowance( 388 | spender: PromiseOrValue, 389 | addedValue: PromiseOrValue, 390 | overrides?: Overrides & { from?: PromiseOrValue } 391 | ): Promise; 392 | 393 | name(overrides?: CallOverrides): Promise; 394 | 395 | symbol(overrides?: CallOverrides): Promise; 396 | 397 | totalSupply(overrides?: CallOverrides): Promise; 398 | 399 | transfer( 400 | recipient: PromiseOrValue, 401 | amount: PromiseOrValue, 402 | overrides?: Overrides & { from?: PromiseOrValue } 403 | ): Promise; 404 | 405 | transferFrom( 406 | sender: PromiseOrValue, 407 | recipient: PromiseOrValue, 408 | amount: PromiseOrValue, 409 | overrides?: Overrides & { from?: PromiseOrValue } 410 | ): Promise; 411 | }; 412 | 413 | populateTransaction: { 414 | allowance( 415 | owner: PromiseOrValue, 416 | spender: PromiseOrValue, 417 | overrides?: CallOverrides 418 | ): Promise; 419 | 420 | approve( 421 | spender: PromiseOrValue, 422 | amount: PromiseOrValue, 423 | overrides?: Overrides & { from?: PromiseOrValue } 424 | ): Promise; 425 | 426 | balanceOf( 427 | account: PromiseOrValue, 428 | overrides?: CallOverrides 429 | ): Promise; 430 | 431 | decimals(overrides?: CallOverrides): Promise; 432 | 433 | decreaseAllowance( 434 | spender: PromiseOrValue, 435 | subtractedValue: PromiseOrValue, 436 | overrides?: Overrides & { from?: PromiseOrValue } 437 | ): Promise; 438 | 439 | increaseAllowance( 440 | spender: PromiseOrValue, 441 | addedValue: PromiseOrValue, 442 | overrides?: Overrides & { from?: PromiseOrValue } 443 | ): Promise; 444 | 445 | name(overrides?: CallOverrides): Promise; 446 | 447 | symbol(overrides?: CallOverrides): Promise; 448 | 449 | totalSupply(overrides?: CallOverrides): Promise; 450 | 451 | transfer( 452 | recipient: PromiseOrValue, 453 | amount: PromiseOrValue, 454 | overrides?: Overrides & { from?: PromiseOrValue } 455 | ): Promise; 456 | 457 | transferFrom( 458 | sender: PromiseOrValue, 459 | recipient: PromiseOrValue, 460 | amount: PromiseOrValue, 461 | overrides?: Overrides & { from?: PromiseOrValue } 462 | ): Promise; 463 | }; 464 | } 465 | -------------------------------------------------------------------------------- /src/config/abi/types/MutilTransfer.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BigNumberish, 8 | BytesLike, 9 | CallOverrides, 10 | ContractTransaction, 11 | Overrides, 12 | PayableOverrides, 13 | PopulatedTransaction, 14 | Signer, 15 | utils, 16 | } from "ethers"; 17 | import type { 18 | FunctionFragment, 19 | Result, 20 | EventFragment, 21 | } from "@ethersproject/abi"; 22 | import type { Listener, Provider } from "@ethersproject/providers"; 23 | import type { 24 | TypedEventFilter, 25 | TypedEvent, 26 | TypedListener, 27 | OnEvent, 28 | PromiseOrValue, 29 | } from "./common"; 30 | 31 | export interface MutilTransferInterface extends utils.Interface { 32 | functions: { 33 | "fee()": FunctionFragment; 34 | "owner()": FunctionFragment; 35 | "renounceOwnership()": FunctionFragment; 36 | "setFee(uint256)": FunctionFragment; 37 | "transferETH(address[],uint256)": FunctionFragment; 38 | "transferMultiETH(address[],uint256[])": FunctionFragment; 39 | "transferMultiToken(address,address[],uint256[])": FunctionFragment; 40 | "transferOwnership(address)": FunctionFragment; 41 | "transferToken(address,address[],uint256)": FunctionFragment; 42 | }; 43 | 44 | getFunction( 45 | nameOrSignatureOrTopic: 46 | | "fee" 47 | | "owner" 48 | | "renounceOwnership" 49 | | "setFee" 50 | | "transferETH" 51 | | "transferMultiETH" 52 | | "transferMultiToken" 53 | | "transferOwnership" 54 | | "transferToken" 55 | ): FunctionFragment; 56 | 57 | encodeFunctionData(functionFragment: "fee", values?: undefined): string; 58 | encodeFunctionData(functionFragment: "owner", values?: undefined): string; 59 | encodeFunctionData( 60 | functionFragment: "renounceOwnership", 61 | values?: undefined 62 | ): string; 63 | encodeFunctionData( 64 | functionFragment: "setFee", 65 | values: [PromiseOrValue] 66 | ): string; 67 | encodeFunctionData( 68 | functionFragment: "transferETH", 69 | values: [PromiseOrValue[], PromiseOrValue] 70 | ): string; 71 | encodeFunctionData( 72 | functionFragment: "transferMultiETH", 73 | values: [PromiseOrValue[], PromiseOrValue[]] 74 | ): string; 75 | encodeFunctionData( 76 | functionFragment: "transferMultiToken", 77 | values: [ 78 | PromiseOrValue, 79 | PromiseOrValue[], 80 | PromiseOrValue[] 81 | ] 82 | ): string; 83 | encodeFunctionData( 84 | functionFragment: "transferOwnership", 85 | values: [PromiseOrValue] 86 | ): string; 87 | encodeFunctionData( 88 | functionFragment: "transferToken", 89 | values: [ 90 | PromiseOrValue, 91 | PromiseOrValue[], 92 | PromiseOrValue 93 | ] 94 | ): string; 95 | 96 | decodeFunctionResult(functionFragment: "fee", data: BytesLike): Result; 97 | decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; 98 | decodeFunctionResult( 99 | functionFragment: "renounceOwnership", 100 | data: BytesLike 101 | ): Result; 102 | decodeFunctionResult(functionFragment: "setFee", data: BytesLike): Result; 103 | decodeFunctionResult( 104 | functionFragment: "transferETH", 105 | data: BytesLike 106 | ): Result; 107 | decodeFunctionResult( 108 | functionFragment: "transferMultiETH", 109 | data: BytesLike 110 | ): Result; 111 | decodeFunctionResult( 112 | functionFragment: "transferMultiToken", 113 | data: BytesLike 114 | ): Result; 115 | decodeFunctionResult( 116 | functionFragment: "transferOwnership", 117 | data: BytesLike 118 | ): Result; 119 | decodeFunctionResult( 120 | functionFragment: "transferToken", 121 | data: BytesLike 122 | ): Result; 123 | 124 | events: { 125 | "OwnershipTransferred(address,address)": EventFragment; 126 | }; 127 | 128 | getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment; 129 | } 130 | 131 | export interface OwnershipTransferredEventObject { 132 | previousOwner: string; 133 | newOwner: string; 134 | } 135 | export type OwnershipTransferredEvent = TypedEvent< 136 | [string, string], 137 | OwnershipTransferredEventObject 138 | >; 139 | 140 | export type OwnershipTransferredEventFilter = 141 | TypedEventFilter; 142 | 143 | export interface MutilTransfer extends BaseContract { 144 | connect(signerOrProvider: Signer | Provider | string): this; 145 | attach(addressOrName: string): this; 146 | deployed(): Promise; 147 | 148 | interface: MutilTransferInterface; 149 | 150 | queryFilter( 151 | event: TypedEventFilter, 152 | fromBlockOrBlockhash?: string | number | undefined, 153 | toBlock?: string | number | undefined 154 | ): Promise>; 155 | 156 | listeners( 157 | eventFilter?: TypedEventFilter 158 | ): Array>; 159 | listeners(eventName?: string): Array; 160 | removeAllListeners( 161 | eventFilter: TypedEventFilter 162 | ): this; 163 | removeAllListeners(eventName?: string): this; 164 | off: OnEvent; 165 | on: OnEvent; 166 | once: OnEvent; 167 | removeListener: OnEvent; 168 | 169 | functions: { 170 | fee(overrides?: CallOverrides): Promise<[BigNumber]>; 171 | 172 | owner(overrides?: CallOverrides): Promise<[string]>; 173 | 174 | renounceOwnership( 175 | overrides?: Overrides & { from?: PromiseOrValue } 176 | ): Promise; 177 | 178 | setFee( 179 | _fee: PromiseOrValue, 180 | overrides?: Overrides & { from?: PromiseOrValue } 181 | ): Promise; 182 | 183 | transferETH( 184 | to: PromiseOrValue[], 185 | amount: PromiseOrValue, 186 | overrides?: PayableOverrides & { from?: PromiseOrValue } 187 | ): Promise; 188 | 189 | transferMultiETH( 190 | to: PromiseOrValue[], 191 | amount: PromiseOrValue[], 192 | overrides?: PayableOverrides & { from?: PromiseOrValue } 193 | ): Promise; 194 | 195 | transferMultiToken( 196 | _token: PromiseOrValue, 197 | to: PromiseOrValue[], 198 | amount: PromiseOrValue[], 199 | overrides?: PayableOverrides & { from?: PromiseOrValue } 200 | ): Promise; 201 | 202 | transferOwnership( 203 | newOwner: PromiseOrValue, 204 | overrides?: Overrides & { from?: PromiseOrValue } 205 | ): Promise; 206 | 207 | transferToken( 208 | _token: PromiseOrValue, 209 | to: PromiseOrValue[], 210 | amount: PromiseOrValue, 211 | overrides?: PayableOverrides & { from?: PromiseOrValue } 212 | ): Promise; 213 | }; 214 | 215 | fee(overrides?: CallOverrides): Promise; 216 | 217 | owner(overrides?: CallOverrides): Promise; 218 | 219 | renounceOwnership( 220 | overrides?: Overrides & { from?: PromiseOrValue } 221 | ): Promise; 222 | 223 | setFee( 224 | _fee: PromiseOrValue, 225 | overrides?: Overrides & { from?: PromiseOrValue } 226 | ): Promise; 227 | 228 | transferETH( 229 | to: PromiseOrValue[], 230 | amount: PromiseOrValue, 231 | overrides?: PayableOverrides & { from?: PromiseOrValue } 232 | ): Promise; 233 | 234 | transferMultiETH( 235 | to: PromiseOrValue[], 236 | amount: PromiseOrValue[], 237 | overrides?: PayableOverrides & { from?: PromiseOrValue } 238 | ): Promise; 239 | 240 | transferMultiToken( 241 | _token: PromiseOrValue, 242 | to: PromiseOrValue[], 243 | amount: PromiseOrValue[], 244 | overrides?: PayableOverrides & { from?: PromiseOrValue } 245 | ): Promise; 246 | 247 | transferOwnership( 248 | newOwner: PromiseOrValue, 249 | overrides?: Overrides & { from?: PromiseOrValue } 250 | ): Promise; 251 | 252 | transferToken( 253 | _token: PromiseOrValue, 254 | to: PromiseOrValue[], 255 | amount: PromiseOrValue, 256 | overrides?: PayableOverrides & { from?: PromiseOrValue } 257 | ): Promise; 258 | 259 | callStatic: { 260 | fee(overrides?: CallOverrides): Promise; 261 | 262 | owner(overrides?: CallOverrides): Promise; 263 | 264 | renounceOwnership(overrides?: CallOverrides): Promise; 265 | 266 | setFee( 267 | _fee: PromiseOrValue, 268 | overrides?: CallOverrides 269 | ): Promise; 270 | 271 | transferETH( 272 | to: PromiseOrValue[], 273 | amount: PromiseOrValue, 274 | overrides?: CallOverrides 275 | ): Promise; 276 | 277 | transferMultiETH( 278 | to: PromiseOrValue[], 279 | amount: PromiseOrValue[], 280 | overrides?: CallOverrides 281 | ): Promise; 282 | 283 | transferMultiToken( 284 | _token: PromiseOrValue, 285 | to: PromiseOrValue[], 286 | amount: PromiseOrValue[], 287 | overrides?: CallOverrides 288 | ): Promise; 289 | 290 | transferOwnership( 291 | newOwner: PromiseOrValue, 292 | overrides?: CallOverrides 293 | ): Promise; 294 | 295 | transferToken( 296 | _token: PromiseOrValue, 297 | to: PromiseOrValue[], 298 | amount: PromiseOrValue, 299 | overrides?: CallOverrides 300 | ): Promise; 301 | }; 302 | 303 | filters: { 304 | "OwnershipTransferred(address,address)"( 305 | previousOwner?: PromiseOrValue | null, 306 | newOwner?: PromiseOrValue | null 307 | ): OwnershipTransferredEventFilter; 308 | OwnershipTransferred( 309 | previousOwner?: PromiseOrValue | null, 310 | newOwner?: PromiseOrValue | null 311 | ): OwnershipTransferredEventFilter; 312 | }; 313 | 314 | estimateGas: { 315 | fee(overrides?: CallOverrides): Promise; 316 | 317 | owner(overrides?: CallOverrides): Promise; 318 | 319 | renounceOwnership( 320 | overrides?: Overrides & { from?: PromiseOrValue } 321 | ): Promise; 322 | 323 | setFee( 324 | _fee: PromiseOrValue, 325 | overrides?: Overrides & { from?: PromiseOrValue } 326 | ): Promise; 327 | 328 | transferETH( 329 | to: PromiseOrValue[], 330 | amount: PromiseOrValue, 331 | overrides?: PayableOverrides & { from?: PromiseOrValue } 332 | ): Promise; 333 | 334 | transferMultiETH( 335 | to: PromiseOrValue[], 336 | amount: PromiseOrValue[], 337 | overrides?: PayableOverrides & { from?: PromiseOrValue } 338 | ): Promise; 339 | 340 | transferMultiToken( 341 | _token: PromiseOrValue, 342 | to: PromiseOrValue[], 343 | amount: PromiseOrValue[], 344 | overrides?: PayableOverrides & { from?: PromiseOrValue } 345 | ): Promise; 346 | 347 | transferOwnership( 348 | newOwner: PromiseOrValue, 349 | overrides?: Overrides & { from?: PromiseOrValue } 350 | ): Promise; 351 | 352 | transferToken( 353 | _token: PromiseOrValue, 354 | to: PromiseOrValue[], 355 | amount: PromiseOrValue, 356 | overrides?: PayableOverrides & { from?: PromiseOrValue } 357 | ): Promise; 358 | }; 359 | 360 | populateTransaction: { 361 | fee(overrides?: CallOverrides): Promise; 362 | 363 | owner(overrides?: CallOverrides): Promise; 364 | 365 | renounceOwnership( 366 | overrides?: Overrides & { from?: PromiseOrValue } 367 | ): Promise; 368 | 369 | setFee( 370 | _fee: PromiseOrValue, 371 | overrides?: Overrides & { from?: PromiseOrValue } 372 | ): Promise; 373 | 374 | transferETH( 375 | to: PromiseOrValue[], 376 | amount: PromiseOrValue, 377 | overrides?: PayableOverrides & { from?: PromiseOrValue } 378 | ): Promise; 379 | 380 | transferMultiETH( 381 | to: PromiseOrValue[], 382 | amount: PromiseOrValue[], 383 | overrides?: PayableOverrides & { from?: PromiseOrValue } 384 | ): Promise; 385 | 386 | transferMultiToken( 387 | _token: PromiseOrValue, 388 | to: PromiseOrValue[], 389 | amount: PromiseOrValue[], 390 | overrides?: PayableOverrides & { from?: PromiseOrValue } 391 | ): Promise; 392 | 393 | transferOwnership( 394 | newOwner: PromiseOrValue, 395 | overrides?: Overrides & { from?: PromiseOrValue } 396 | ): Promise; 397 | 398 | transferToken( 399 | _token: PromiseOrValue, 400 | to: PromiseOrValue[], 401 | amount: PromiseOrValue, 402 | overrides?: PayableOverrides & { from?: PromiseOrValue } 403 | ): Promise; 404 | }; 405 | } 406 | -------------------------------------------------------------------------------- /src/config/abi/types/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/config/abi/types/factories/Erc20__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { Erc20, Erc20Interface } from "../Erc20"; 8 | 9 | const _abi = [ 10 | { 11 | inputs: [ 12 | { 13 | internalType: "string", 14 | name: "_name", 15 | type: "string", 16 | }, 17 | { 18 | internalType: "string", 19 | name: "_symbol", 20 | type: "string", 21 | }, 22 | ], 23 | payable: false, 24 | stateMutability: "nonpayable", 25 | type: "constructor", 26 | }, 27 | { 28 | anonymous: false, 29 | inputs: [ 30 | { 31 | indexed: true, 32 | internalType: "address", 33 | name: "owner", 34 | type: "address", 35 | }, 36 | { 37 | indexed: true, 38 | internalType: "address", 39 | name: "spender", 40 | type: "address", 41 | }, 42 | { 43 | indexed: false, 44 | internalType: "uint256", 45 | name: "value", 46 | type: "uint256", 47 | }, 48 | ], 49 | name: "Approval", 50 | type: "event", 51 | }, 52 | { 53 | anonymous: false, 54 | inputs: [ 55 | { 56 | indexed: true, 57 | internalType: "address", 58 | name: "from", 59 | type: "address", 60 | }, 61 | { 62 | indexed: true, 63 | internalType: "address", 64 | name: "to", 65 | type: "address", 66 | }, 67 | { 68 | indexed: false, 69 | internalType: "uint256", 70 | name: "value", 71 | type: "uint256", 72 | }, 73 | ], 74 | name: "Transfer", 75 | type: "event", 76 | }, 77 | { 78 | constant: true, 79 | inputs: [ 80 | { 81 | internalType: "address", 82 | name: "owner", 83 | type: "address", 84 | }, 85 | { 86 | internalType: "address", 87 | name: "spender", 88 | type: "address", 89 | }, 90 | ], 91 | name: "allowance", 92 | outputs: [ 93 | { 94 | internalType: "uint256", 95 | name: "", 96 | type: "uint256", 97 | }, 98 | ], 99 | payable: false, 100 | stateMutability: "view", 101 | type: "function", 102 | }, 103 | { 104 | constant: false, 105 | inputs: [ 106 | { 107 | internalType: "address", 108 | name: "spender", 109 | type: "address", 110 | }, 111 | { 112 | internalType: "uint256", 113 | name: "amount", 114 | type: "uint256", 115 | }, 116 | ], 117 | name: "approve", 118 | outputs: [ 119 | { 120 | internalType: "bool", 121 | name: "", 122 | type: "bool", 123 | }, 124 | ], 125 | payable: false, 126 | stateMutability: "nonpayable", 127 | type: "function", 128 | }, 129 | { 130 | constant: true, 131 | inputs: [ 132 | { 133 | internalType: "address", 134 | name: "account", 135 | type: "address", 136 | }, 137 | ], 138 | name: "balanceOf", 139 | outputs: [ 140 | { 141 | internalType: "uint256", 142 | name: "", 143 | type: "uint256", 144 | }, 145 | ], 146 | payable: false, 147 | stateMutability: "view", 148 | type: "function", 149 | }, 150 | { 151 | constant: true, 152 | inputs: [], 153 | name: "decimals", 154 | outputs: [ 155 | { 156 | internalType: "uint256", 157 | name: "", 158 | type: "uint256", 159 | }, 160 | ], 161 | payable: false, 162 | stateMutability: "view", 163 | type: "function", 164 | }, 165 | { 166 | constant: false, 167 | inputs: [ 168 | { 169 | internalType: "address", 170 | name: "spender", 171 | type: "address", 172 | }, 173 | { 174 | internalType: "uint256", 175 | name: "subtractedValue", 176 | type: "uint256", 177 | }, 178 | ], 179 | name: "decreaseAllowance", 180 | outputs: [ 181 | { 182 | internalType: "bool", 183 | name: "", 184 | type: "bool", 185 | }, 186 | ], 187 | payable: false, 188 | stateMutability: "nonpayable", 189 | type: "function", 190 | }, 191 | { 192 | constant: false, 193 | inputs: [ 194 | { 195 | internalType: "address", 196 | name: "spender", 197 | type: "address", 198 | }, 199 | { 200 | internalType: "uint256", 201 | name: "addedValue", 202 | type: "uint256", 203 | }, 204 | ], 205 | name: "increaseAllowance", 206 | outputs: [ 207 | { 208 | internalType: "bool", 209 | name: "", 210 | type: "bool", 211 | }, 212 | ], 213 | payable: false, 214 | stateMutability: "nonpayable", 215 | type: "function", 216 | }, 217 | { 218 | constant: true, 219 | inputs: [], 220 | name: "name", 221 | outputs: [ 222 | { 223 | internalType: "string", 224 | name: "", 225 | type: "string", 226 | }, 227 | ], 228 | payable: false, 229 | stateMutability: "view", 230 | type: "function", 231 | }, 232 | { 233 | constant: true, 234 | inputs: [], 235 | name: "symbol", 236 | outputs: [ 237 | { 238 | internalType: "string", 239 | name: "", 240 | type: "string", 241 | }, 242 | ], 243 | payable: false, 244 | stateMutability: "view", 245 | type: "function", 246 | }, 247 | { 248 | constant: true, 249 | inputs: [], 250 | name: "totalSupply", 251 | outputs: [ 252 | { 253 | internalType: "uint256", 254 | name: "", 255 | type: "uint256", 256 | }, 257 | ], 258 | payable: false, 259 | stateMutability: "view", 260 | type: "function", 261 | }, 262 | { 263 | constant: false, 264 | inputs: [ 265 | { 266 | internalType: "address", 267 | name: "recipient", 268 | type: "address", 269 | }, 270 | { 271 | internalType: "uint256", 272 | name: "amount", 273 | type: "uint256", 274 | }, 275 | ], 276 | name: "transfer", 277 | outputs: [ 278 | { 279 | internalType: "bool", 280 | name: "", 281 | type: "bool", 282 | }, 283 | ], 284 | payable: false, 285 | stateMutability: "nonpayable", 286 | type: "function", 287 | }, 288 | { 289 | constant: false, 290 | inputs: [ 291 | { 292 | internalType: "address", 293 | name: "sender", 294 | type: "address", 295 | }, 296 | { 297 | internalType: "address", 298 | name: "recipient", 299 | type: "address", 300 | }, 301 | { 302 | internalType: "uint256", 303 | name: "amount", 304 | type: "uint256", 305 | }, 306 | ], 307 | name: "transferFrom", 308 | outputs: [ 309 | { 310 | internalType: "bool", 311 | name: "", 312 | type: "bool", 313 | }, 314 | ], 315 | payable: false, 316 | stateMutability: "nonpayable", 317 | type: "function", 318 | }, 319 | ]; 320 | 321 | export class Erc20__factory { 322 | static readonly abi = _abi; 323 | static createInterface(): Erc20Interface { 324 | return new utils.Interface(_abi) as Erc20Interface; 325 | } 326 | static connect(address: string, signerOrProvider: Signer | Provider): Erc20 { 327 | return new Contract(address, _abi, signerOrProvider) as Erc20; 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /src/config/abi/types/factories/MutilTransfer__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { MutilTransfer, MutilTransferInterface } from "../MutilTransfer"; 8 | 9 | const _abi = [ 10 | { 11 | anonymous: false, 12 | inputs: [ 13 | { 14 | indexed: true, 15 | internalType: "address", 16 | name: "previousOwner", 17 | type: "address", 18 | }, 19 | { 20 | indexed: true, 21 | internalType: "address", 22 | name: "newOwner", 23 | type: "address", 24 | }, 25 | ], 26 | name: "OwnershipTransferred", 27 | type: "event", 28 | }, 29 | { 30 | inputs: [], 31 | name: "fee", 32 | outputs: [ 33 | { 34 | internalType: "uint256", 35 | name: "", 36 | type: "uint256", 37 | }, 38 | ], 39 | stateMutability: "view", 40 | type: "function", 41 | }, 42 | { 43 | inputs: [], 44 | name: "owner", 45 | outputs: [ 46 | { 47 | internalType: "address", 48 | name: "", 49 | type: "address", 50 | }, 51 | ], 52 | stateMutability: "view", 53 | type: "function", 54 | }, 55 | { 56 | inputs: [], 57 | name: "renounceOwnership", 58 | outputs: [], 59 | stateMutability: "nonpayable", 60 | type: "function", 61 | }, 62 | { 63 | inputs: [ 64 | { 65 | internalType: "uint256", 66 | name: "_fee", 67 | type: "uint256", 68 | }, 69 | ], 70 | name: "setFee", 71 | outputs: [], 72 | stateMutability: "nonpayable", 73 | type: "function", 74 | }, 75 | { 76 | inputs: [ 77 | { 78 | internalType: "address payable[]", 79 | name: "to", 80 | type: "address[]", 81 | }, 82 | { 83 | internalType: "uint256", 84 | name: "amount", 85 | type: "uint256", 86 | }, 87 | ], 88 | name: "transferETH", 89 | outputs: [], 90 | stateMutability: "payable", 91 | type: "function", 92 | }, 93 | { 94 | inputs: [ 95 | { 96 | internalType: "address payable[]", 97 | name: "to", 98 | type: "address[]", 99 | }, 100 | { 101 | internalType: "uint256[]", 102 | name: "amount", 103 | type: "uint256[]", 104 | }, 105 | ], 106 | name: "transferMultiETH", 107 | outputs: [], 108 | stateMutability: "payable", 109 | type: "function", 110 | }, 111 | { 112 | inputs: [ 113 | { 114 | internalType: "address", 115 | name: "_token", 116 | type: "address", 117 | }, 118 | { 119 | internalType: "address[]", 120 | name: "to", 121 | type: "address[]", 122 | }, 123 | { 124 | internalType: "uint256[]", 125 | name: "amount", 126 | type: "uint256[]", 127 | }, 128 | ], 129 | name: "transferMultiToken", 130 | outputs: [], 131 | stateMutability: "payable", 132 | type: "function", 133 | }, 134 | { 135 | inputs: [ 136 | { 137 | internalType: "address", 138 | name: "newOwner", 139 | type: "address", 140 | }, 141 | ], 142 | name: "transferOwnership", 143 | outputs: [], 144 | stateMutability: "nonpayable", 145 | type: "function", 146 | }, 147 | { 148 | inputs: [ 149 | { 150 | internalType: "address", 151 | name: "_token", 152 | type: "address", 153 | }, 154 | { 155 | internalType: "address[]", 156 | name: "to", 157 | type: "address[]", 158 | }, 159 | { 160 | internalType: "uint256", 161 | name: "amount", 162 | type: "uint256", 163 | }, 164 | ], 165 | name: "transferToken", 166 | outputs: [], 167 | stateMutability: "payable", 168 | type: "function", 169 | }, 170 | ]; 171 | 172 | export class MutilTransfer__factory { 173 | static readonly abi = _abi; 174 | static createInterface(): MutilTransferInterface { 175 | return new utils.Interface(_abi) as MutilTransferInterface; 176 | } 177 | static connect( 178 | address: string, 179 | signerOrProvider: Signer | Provider 180 | ): MutilTransfer { 181 | return new Contract(address, _abi, signerOrProvider) as MutilTransfer; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/config/abi/types/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { MutilTransfer__factory } from "./MutilTransfer__factory"; 5 | export { Erc20__factory } from "./Erc20__factory"; 6 | -------------------------------------------------------------------------------- /src/config/abi/types/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { MutilTransfer } from "./MutilTransfer"; 5 | export type { Erc20 } from "./Erc20"; 6 | export * as factories from "./factories"; 7 | export { Erc20__factory } from "./factories/Erc20__factory"; 8 | export { MutilTransfer__factory } from "./factories/MutilTransfer__factory"; 9 | -------------------------------------------------------------------------------- /src/config/connectors/NetworkConnector.ts: -------------------------------------------------------------------------------- 1 | import { ConnectorUpdate } from "@web3-react/types"; 2 | import { AbstractConnector } from "@web3-react/abstract-connector"; 3 | import invariant from "tiny-invariant"; 4 | 5 | interface NetworkConnectorArguments { 6 | urls: { [chainId: number]: string }; 7 | defaultChainId?: number; 8 | } 9 | 10 | // taken from ethers.js, compatible interface with web3 provider 11 | type AsyncSendable = { 12 | isMetaMask?: boolean; 13 | host?: string; 14 | path?: string; 15 | sendAsync?: (request: any, callback: (error: any, response: any) => void) => void; 16 | send?: (request: any, callback: (error: any, response: any) => void) => void; 17 | }; 18 | 19 | class RequestError extends Error { 20 | constructor(message: string, public code: number, public data?: unknown) { 21 | super(message); 22 | } 23 | } 24 | 25 | interface BatchItem { 26 | request: { jsonrpc: "2.0"; id: number; method: string; params: unknown }; 27 | resolve: (result: any) => void; 28 | reject: (error: Error) => void; 29 | } 30 | 31 | class MiniRpcProvider implements AsyncSendable { 32 | public readonly isMetaMask: false = false; 33 | 34 | public readonly chainId: number; 35 | 36 | public readonly url: string; 37 | 38 | public readonly host: string; 39 | 40 | public readonly path: string; 41 | 42 | public readonly batchWaitTimeMs: number; 43 | 44 | private nextId = 1; 45 | 46 | private batchTimeoutId: ReturnType | null = null; 47 | 48 | private batch: BatchItem[] = []; 49 | 50 | constructor(chainId: number, url: string, batchWaitTimeMs?: number) { 51 | this.chainId = chainId; 52 | this.url = url; 53 | const parsed = new URL(url); 54 | this.host = parsed.host; 55 | this.path = parsed.pathname; // how long to wait to batch calls 56 | this.batchWaitTimeMs = batchWaitTimeMs ?? 50; 57 | } 58 | 59 | public readonly clearBatch = async () => { 60 | // console.info('Clearing batch', this.batch) 61 | const { batch } = this; 62 | this.batch = []; 63 | this.batchTimeoutId = null; 64 | let response: Response; 65 | try { 66 | response = await fetch(this.url, { 67 | method: "POST", 68 | headers: { "content-type": "application/json", accept: "application/json" }, 69 | body: JSON.stringify(batch.map((item) => item.request)), 70 | }); 71 | } catch (error) { 72 | batch.forEach(({ reject }) => reject(new Error("Failed to send batch call"))); 73 | return; 74 | } 75 | 76 | if (!response.ok) { 77 | batch.forEach(({ reject }) => 78 | reject(new RequestError(`${response.status}: ${response.statusText}`, -32000)), 79 | ); 80 | return; 81 | } 82 | 83 | let json; 84 | try { 85 | json = await response.json(); 86 | } catch (error) { 87 | batch.forEach(({ reject }) => reject(new Error("Failed to parse JSON response"))); 88 | return; 89 | } 90 | const byKey = batch.reduce<{ [id: number]: BatchItem }>((memo, current) => { 91 | memo[current.request.id] = current; 92 | return memo; 93 | }, {}); // eslint-disable-next-line no-restricted-syntax 94 | for (const result of json) { 95 | const { 96 | resolve, 97 | reject, 98 | request: { method }, 99 | } = byKey[result.id]; 100 | if (resolve) { 101 | if ("error" in result) { 102 | reject(new RequestError(result?.error?.message, result?.error?.code, result?.error?.data)); 103 | } else if ("result" in result) { 104 | resolve(result.result); 105 | } else { 106 | reject( 107 | new RequestError(`Received unexpected JSON-RPC response to ${method} request.`, -32000, result), 108 | ); 109 | } 110 | } 111 | } 112 | }; 113 | 114 | public readonly sendAsync = ( 115 | request: { jsonrpc: "2.0"; id: number | string | null; method: string; params?: any }, 116 | callback: (error: any, response: any) => void, 117 | ): void => { 118 | this.request(request.method, request.params) 119 | .then((result) => callback(null, { jsonrpc: "2.0", id: request.id, result })) 120 | .catch((error) => callback(error, null)); 121 | }; 122 | 123 | public readonly request = async ( 124 | method: string | { method: string; params: unknown[] }, 125 | params?: any, 126 | ): Promise => { 127 | if (typeof method !== "string") { 128 | return this.request(method.method, method.params); 129 | } 130 | if (method === "eth_chainId") { 131 | return `0x${this.chainId.toString(16)}`; 132 | } 133 | const promise = new Promise((resolve, reject) => { 134 | this.batch.push({ 135 | request: { 136 | jsonrpc: "2.0", 137 | id: this.nextId++, 138 | method, 139 | params, 140 | }, 141 | resolve, 142 | reject, 143 | }); 144 | }); 145 | this.batchTimeoutId = this.batchTimeoutId ?? setTimeout(this.clearBatch, this.batchWaitTimeMs); 146 | return promise; 147 | }; 148 | } 149 | 150 | export class NetworkConnector extends AbstractConnector { 151 | private readonly providers: { [chainId: number]: MiniRpcProvider }; 152 | 153 | private currentChainId: number; 154 | 155 | constructor({ urls, defaultChainId }: NetworkConnectorArguments) { 156 | invariant( 157 | defaultChainId || Object.keys(urls).length === 1, 158 | "defaultChainId is a required argument with >1 url", 159 | ); 160 | super({ supportedChainIds: Object.keys(urls).map((k): number => Number(k)) }); 161 | 162 | this.currentChainId = defaultChainId || Number(Object.keys(urls)[0]); 163 | this.providers = Object.keys(urls).reduce<{ [chainId: number]: MiniRpcProvider }>((accumulator, chainId) => { 164 | accumulator[Number(chainId)] = new MiniRpcProvider(Number(chainId), urls[Number(chainId)]); 165 | return accumulator; 166 | }, {}); 167 | } 168 | 169 | public get provider(): MiniRpcProvider { 170 | return this.providers[this.currentChainId]; 171 | } 172 | 173 | public async activate(): Promise { 174 | return { provider: this.providers[this.currentChainId], chainId: this.currentChainId, account: null }; 175 | } 176 | 177 | public async getProvider(): Promise { 178 | return this.providers[this.currentChainId]; 179 | } 180 | 181 | public async getChainId(): Promise { 182 | return this.currentChainId; 183 | } 184 | 185 | public async getAccount(): Promise { 186 | return null; 187 | } 188 | 189 | public deactivate() { 190 | return null; 191 | } 192 | } 193 | 194 | export default NetworkConnector; 195 | -------------------------------------------------------------------------------- /src/config/connectors/index.ts: -------------------------------------------------------------------------------- 1 | export const connectorLocalStorageKey = "connectorId"; 2 | -------------------------------------------------------------------------------- /src/config/constants/chainIcon.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./chainId"; 2 | 3 | const Bsc = "https://raw.githubusercontent.com/sushiswap/icons/master/network/bsc.jpg"; 4 | const Goerli = "https://raw.githubusercontent.com/sushiswap/icons/master/network/goerli.jpg"; 5 | const Kovan = "https://raw.githubusercontent.com/sushiswap/icons/master/network/kovan.jpg"; 6 | const Mainnet = "https://raw.githubusercontent.com/sushiswap/icons/master/network/mainnet.jpg"; 7 | const Rinkeby = "https://raw.githubusercontent.com/sushiswap/icons/master/network/rinkeby.jpg"; 8 | const Ropsten = "https://raw.githubusercontent.com/sushiswap/icons/master/network/ropsten.jpg"; 9 | 10 | export const NETWORK_ICON = { 11 | [ChainId.ETHEREUM]: Mainnet, 12 | [ChainId.ROPSTEN]: Ropsten, 13 | [ChainId.RINKEBY]: Rinkeby, 14 | [ChainId.GOERLI]: Goerli, 15 | [ChainId.KOVAN]: Kovan, 16 | [ChainId.BSC]: Bsc, 17 | [ChainId.BSC_TESTNET]: Bsc, 18 | }; 19 | 20 | export const NETWORK_LABEL: { [ChainId: number]: string } = { 21 | [ChainId.ETHEREUM]: "Ethereum", 22 | [ChainId.RINKEBY]: "Rinkeby", 23 | [ChainId.ROPSTEN]: "Ropsten", 24 | [ChainId.GOERLI]: "Goerli", 25 | [ChainId.KOVAN]: "Kovan", 26 | [ChainId.BSC]: "BSC", 27 | [ChainId.BSC_TESTNET]: "BSC Testnet", 28 | }; 29 | -------------------------------------------------------------------------------- /src/config/constants/chainId.ts: -------------------------------------------------------------------------------- 1 | // export const ChainId = { 2 | // 1: "ETHEREUM", 3 | // 3: "ROPSTEN", 4 | // 4: "RINKEBY", 5 | // 5: "GÖRLI", 6 | // 40: "TELOS", 7 | // 42: "KOVAN", 8 | // 56: "BSC", 9 | // 65: "OKEX_TESTNET", 10 | // 66: "OKEX", 11 | // 97: "BSC_TESTNET", 12 | // 100: "XDAI", 13 | // 122: "FUSE", 14 | // 128: "HECO", 15 | // 137: "MATIC", 16 | // 250: "FANTOM", 17 | // 256: "HECO_TESTNET", 18 | // 1284: "MOONBEAM", 19 | // 1285: "MOONRIVER", 20 | // 1287: "MOONBEAM_TESTNET", 21 | // 4002: "FANTOM_TESTNET", 22 | // 31337: "HARDHAT", 23 | // 42161: "ARBITRUM", 24 | // 42220: "CELO", 25 | // 43113: "AVALANCHE_TESTNET", 26 | // 43114: "AVALANCHE", 27 | // 80001: "MATIC_TESTNET", 28 | // 1666600000: "HARMONY", 29 | // 1666700000: "HARMONY_TESTNET", 30 | // 11297108099: "PALM_TESTNET", 31 | // 11297108109: "PALM", 32 | // 79377087078960: "ARBITRUM_TESTNET", 33 | // ARBITRUM: 42161, 34 | // ARBITRUM_TESTNET: 79377087078960, 35 | // AVALANCHE: 43114, 36 | // AVALANCHE_TESTNET: 43113, 37 | // BSC: 56, 38 | // BSC_TESTNET: 97, 39 | // CELO: 42220, 40 | // ETHEREUM: 1, 41 | // FANTOM: 250, 42 | // FANTOM_TESTNET: 4002, 43 | // FUSE: 122, 44 | // GÖRLI: 5, 45 | // HARDHAT: 31337, 46 | // HARMONY: 1666600000, 47 | // HARMONY_TESTNET: 1666700000, 48 | // HECO: 128, 49 | // HECO_TESTNET: 256, 50 | // KOVAN: 42, 51 | // MATIC: 137, 52 | // MATIC_TESTNET: 80001, 53 | // MOONBEAM: 1284, 54 | // MOONBEAM_TESTNET: 1287, 55 | // MOONRIVER: 1285, 56 | // OKEX: 66, 57 | // OKEX_TESTNET: 65, 58 | // PALM: 11297108109, 59 | // PALM_TESTNET: 11297108099, 60 | // RINKEBY: 4, 61 | // ROPSTEN: 3, 62 | // TELOS: 40, 63 | // XDAI: 100, 64 | // }; 65 | 66 | export enum ChainId { 67 | ETHEREUM = 1, 68 | ROPSTEN = 3, 69 | RINKEBY = 4, 70 | GOERLI = 5, 71 | KOVAN = 42, 72 | // MATIC = 137, 73 | // MATIC_TESTNET = 80001, 74 | // FANTOM = 250, 75 | // FANTOM_TESTNET = 4002, 76 | // XDAI = 100, 77 | BSC = 56, 78 | BSC_TESTNET = 97, 79 | // ARBITRUM = 42161, 80 | // ARBITRUM_TESTNET = 79377087078960, 81 | // MOONBEAM_TESTNET = 1287, 82 | // AVALANCHE = 43114, 83 | // AVALANCHE_TESTNET = 43113, 84 | // HECO = 128, 85 | // HECO_TESTNET = 256, 86 | // HARMONY = 1666600000, 87 | // HARMONY_TESTNET = 1666700000, 88 | // OKEX = 66, 89 | // OKEX_TESTNET = 65, 90 | // CELO = 42220, 91 | // PALM = 11297108109, 92 | // PALM_TESTNET = 11297108099, 93 | // MOONRIVER = 1285, 94 | // FUSE = 122, 95 | // TELOS = 40, 96 | // HARDHAT = 31337, 97 | // MOONBEAM = 1284, 98 | } 99 | -------------------------------------------------------------------------------- /src/config/constants/contractAddresses.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./chainId"; 2 | 3 | export default { 4 | multiTransfer: { 5 | [ChainId.RINKEBY]: "0xB304F14dc0fF9bC596D5d1f0e4D67dCA3278f8cC", 6 | [ChainId.KOVAN]: "0xDffd2226496D02a1108618aeA58F9aA3D5A3538F", 7 | [ChainId.GOERLI]: "0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 8 | [ChainId.BSC_TESTNET]: "0x129beB5ed515aD15Ec74D1b61d02b92aF1b39bd5", 9 | [ChainId.BSC]: "0xEcC118Ce3bCC08e574010100ac7d5eD65160fc70", 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /src/config/constants/defaultChainId.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./chainId"; 2 | 3 | export default ChainId.RINKEBY; 4 | -------------------------------------------------------------------------------- /src/config/constants/native.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@/config/constants/chainId"; 2 | import { Token } from "@/config/constants/types"; 3 | type NativeMap = { [chainId: number]: Token }; 4 | export const NATIVE: NativeMap = { 5 | [ChainId.BSC]: { 6 | name: "BNB Token", 7 | symbol: "BNB", 8 | address: "", 9 | chainId: ChainId.BSC, 10 | decimals: 18, 11 | logoURI: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png", 12 | }, 13 | [ChainId.RINKEBY]: { 14 | name: "ETH Token", 15 | symbol: "ETH", 16 | address: "", 17 | chainId: ChainId.RINKEBY, 18 | decimals: 18, 19 | logoURI: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png", 20 | }, 21 | [ChainId.KOVAN]: { 22 | name: "ETH Token", 23 | symbol: "ETH", 24 | address: "", 25 | chainId: ChainId.KOVAN, 26 | decimals: 18, 27 | logoURI: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png", 28 | }, 29 | // [ChainId.Mainnet]: { 30 | // name: "ETH Token", 31 | // symbol: "ETH", 32 | // address: "", 33 | // chainId: ChainId.Mainnet, 34 | // decimals: 18, 35 | // logoURI: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png", 36 | // }, 37 | [ChainId.BSC_TESTNET]: { 38 | name: "BNB Token", 39 | symbol: "BNB", 40 | address: "", 41 | chainId: ChainId.BSC_TESTNET, 42 | decimals: 18, 43 | logoURI: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png", 44 | }, 45 | }; 46 | 47 | export const getNative = (chainId: number) => { 48 | return NATIVE[chainId] ? NATIVE[chainId] : NATIVE[ChainId.RINKEBY]; 49 | }; 50 | -------------------------------------------------------------------------------- /src/config/constants/rpc.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./chainId"; 2 | 3 | const RPC = { 4 | [ChainId.ETHEREUM]: "https://eth-mainnet.g.alchemy.com/v2/VeGNNvyUMm_WjwEF8UZi8LTsNLTp_i5L", 5 | // [ChainId.ETHEREUM]: 'https://eth-mainnet.alchemyapi.io/v2/HNQXSfiUcPjfpDBQaWYXjqlhTr1cEY9c', 6 | // [ChainId.MAINNET]: 'https://eth-mainnet.alchemyapi.io/v2/q1gSNoSMEzJms47Qn93f9-9Xg5clkmEC', 7 | [ChainId.ROPSTEN]: "https://ropsten.infura.io/v3/c7bae63096c74b3dad54ad7ae275df0c", 8 | [ChainId.RINKEBY]: "https://eth-rinkeby.alchemyapi.io/v2/TWCO2XsIVDteeZA6WcmHPWmMowK6SDTD", 9 | [ChainId.GOERLI]: "https://eth-goerli.g.alchemy.com/v2/cihv7vmXh20-FLOy9gUP69NpU_p9jpRh", 10 | [ChainId.KOVAN]: "https://kovan.infura.io/v3/c7bae63096c74b3dad54ad7ae275df0c", 11 | // [ChainId.FANTOM]: "https://rpcapi.fantom.network", 12 | // [ChainId.FANTOM_TESTNET]: "https://rpc.testnet.fantom.network", 13 | // [ChainId.MATIC]: "https://polygon-rpc.com/", 14 | // [ChainId.MATIC_TESTNET]: "https://rpc-mumbai.matic.today", 15 | // [ChainId.XDAI]: "https://rpc.xdaichain.com", 16 | [ChainId.BSC]: "https://bsc-dataseed.binance.org/", 17 | [ChainId.BSC_TESTNET]: "https://data-seed-prebsc-2-s3.binance.org:8545", 18 | // [ChainId.MOONBEAM_TESTNET]: "https://rpc.testnet.moonbeam.network", 19 | // [ChainId.AVALANCHE]: "https://api.avax.network/ext/bc/C/rpc", 20 | // [ChainId.AVALANCHE_TESTNET]: "https://api.avax-test.network/ext/bc/C/rpc", 21 | // [ChainId.HECO]: "https://http-mainnet.hecochain.com", 22 | // [ChainId.HECO_TESTNET]: "https://http-testnet.hecochain.com", 23 | // [ChainId.HARMONY]: "https://api.harmony.one", 24 | // [ChainId.HARMONY_TESTNET]: "https://api.s0.b.hmny.io", 25 | // [ChainId.OKEX]: "https://exchainrpc.okex.org", 26 | // [ChainId.OKEX_TESTNET]: "https://exchaintestrpc.okex.org", 27 | // [ChainId.ARBITRUM]: "https://arb1.arbitrum.io/rpc", 28 | // [ChainId.PALM]: "https://palm-mainnet.infura.io/v3/da5fbfafcca14b109e2665290681e267", 29 | // [ChainId.FUSE]: "https://rpc.fuse.io", 30 | // [ChainId.CELO]: "https://forno.celo.org", 31 | // [ChainId.MOONRIVER]: "https://rpc.moonriver.moonbeam.network", 32 | // [ChainId.TELOS]: "https://mainnet.telos.net/evm", 33 | // [ChainId.MOONBEAM]: "https://rpc.api.moonbeam.network", 34 | }; 35 | 36 | export default RPC; 37 | -------------------------------------------------------------------------------- /src/config/constants/types.ts: -------------------------------------------------------------------------------- 1 | export interface Token { 2 | address: string; 3 | name: string; 4 | symbol: string; 5 | decimals: number; 6 | logoURI?: string; 7 | chainId: number | undefined; 8 | } 9 | export interface Address { 10 | // 97?: string; 11 | // 56: string; 12 | 4: string; 13 | } 14 | -------------------------------------------------------------------------------- /src/config/constants/wallets.ts: -------------------------------------------------------------------------------- 1 | import { NetworkConnector } from "@/config/connectors/NetworkConnector"; 2 | import { AbstractConnector } from "@web3-react/abstract-connector"; 3 | import { InjectedConnector } from "@web3-react/injected-connector"; 4 | import { ChainId } from "./chainId"; 5 | import RPC from "./rpc"; 6 | 7 | const supportedChainIds = Object.values(ChainId) as number[]; 8 | 9 | export const network = new NetworkConnector({ 10 | defaultChainId: 4, 11 | urls: RPC, 12 | }); 13 | 14 | export const injected = new InjectedConnector({ 15 | supportedChainIds, 16 | }); 17 | 18 | export interface WalletInfo { 19 | connector?: (() => Promise) | AbstractConnector; 20 | name: string; 21 | iconName: string; 22 | description: string; 23 | href: string | null; 24 | color: string; 25 | primary?: true; 26 | mobile?: true; 27 | mobileOnly?: true; 28 | } 29 | 30 | export const SUPPORTED_WALLETS: { [key: string]: WalletInfo } = { 31 | INJECTED: { 32 | connector: injected, 33 | name: "Injected", 34 | iconName: "injected.svg", 35 | description: "Injected web3 provider.", 36 | href: null, 37 | color: "#010101", 38 | primary: true, 39 | }, 40 | METAMASK: { 41 | connector: injected, 42 | name: "MetaMask", 43 | iconName: "metamask.png", 44 | description: "Easy-to-use browser extension.", 45 | href: null, 46 | color: "#E8831D", 47 | }, 48 | METAMASK_MOBILE: { 49 | name: "MetaMask", 50 | iconName: "metamask.png", 51 | description: "Open in MetaMask app.", 52 | href: "https://metamask.app.link/dapp/app.sushi.com", 53 | color: "#E8831D", 54 | mobile: true, 55 | mobileOnly: true, 56 | }, 57 | // WALLET_CONNECT: { 58 | // connector: async () => { 59 | // const WalletConnectConnector = (await import("@web3-react/walletconnect-connector")).WalletConnectConnector; 60 | // return new WalletConnectConnector({ 61 | // rpc: RPC, 62 | // bridge: "https://bridge.walletconnect.org", 63 | // qrcode: true, 64 | // supportedChainIds, 65 | // }); 66 | // }, 67 | // name: "WalletConnect", 68 | // iconName: "wallet-connect.svg", 69 | // description: "Connect to Trust Wallet, Rainbow Wallet and more...", 70 | // href: null, 71 | // color: "#4196FC", 72 | // mobile: true, 73 | // }, 74 | // KEYSTONE: { 75 | // connector: async () => { 76 | // const KeystoneConnector = (await import("@keystonehq/keystone-connector")).KeystoneConnector; 77 | // return new KeystoneConnector({ 78 | // chainId: 1, 79 | // url: RPC[ChainId.ETHEREUM], 80 | // }); 81 | // }, 82 | // name: "Keystone", 83 | // iconName: "keystone.png", 84 | // description: "Connect to Keystone hardware wallet.", 85 | // href: null, 86 | // color: "#4196FC", 87 | // mobile: true, 88 | // }, 89 | // LATTICE: { 90 | // connector: async () => { 91 | // const LatticeConnector = (await import("@web3-react/lattice-connector")).LatticeConnector; 92 | // return new LatticeConnector({ 93 | // chainId: 1, 94 | // url: RPC[ChainId.ETHEREUM], 95 | // appName: "SushiSwap", 96 | // }); 97 | // }, 98 | // name: "Lattice", 99 | // iconName: "lattice.png", 100 | // description: "Connect to GridPlus Wallet.", 101 | // href: null, 102 | // color: "#40a9ff", 103 | // mobile: true, 104 | // }, 105 | // WALLET_LINK: { 106 | // connector: async () => { 107 | // const WalletLinkConnector = (await import("@web3-react/walletlink-connector")).WalletLinkConnector; 108 | // return new WalletLinkConnector({ 109 | // url: RPC[ChainId.ETHEREUM], 110 | // appName: "SushiSwap", 111 | // appLogoUrl: "https://raw.githubusercontent.com/sushiswap/art/master/sushi/logo-256x256.png", 112 | // darkMode: true, 113 | // }); 114 | // }, 115 | // name: "Coinbase Wallet", 116 | // iconName: "coinbase.svg", 117 | // description: "Use Coinbase Wallet app on mobile device", 118 | // href: null, 119 | // color: "#315CF5", 120 | // }, 121 | // COINBASE_LINK: { 122 | // name: "Open in Coinbase Wallet", 123 | // iconName: "coinbase.svg", 124 | // description: "Open in Coinbase Wallet app.", 125 | // href: "https://go.cb-w.com", 126 | // color: "#315CF5", 127 | // mobile: true, 128 | // mobileOnly: true, 129 | // }, 130 | // FORTMATIC: { 131 | // connector: async () => { 132 | // const FortmaticConnector = (await import("@web3-react/fortmatic-connector")).FortmaticConnector; 133 | // return new FortmaticConnector({ 134 | // apiKey: process.env.NEXT_PUBLIC_FORTMATIC_API_KEY ?? "", 135 | // chainId: 1, 136 | // }); 137 | // }, 138 | // name: "Fortmatic", 139 | // iconName: "fortmatic.png", 140 | // description: "Login using Fortmatic hosted wallet", 141 | // href: null, 142 | // color: "#6748FF", 143 | // mobile: true, 144 | // }, 145 | // Portis: { 146 | // connector: async () => { 147 | // const PortisConnector = (await import("@web3-react/portis-connector")).PortisConnector; 148 | // return new PortisConnector({ 149 | // dAppId: process.env.NEXT_PUBLIC_PORTIS_ID ?? "", 150 | // networks: [1], 151 | // }); 152 | // }, 153 | // name: "Portis", 154 | // iconName: "portis.png", 155 | // description: "Login using Portis hosted wallet", 156 | // href: null, 157 | // color: "#4A6C9B", 158 | // mobile: true, 159 | // }, 160 | // Torus: { 161 | // connector: async () => { 162 | // const TorusConnector = (await import("@web3-react/torus-connector")).TorusConnector; 163 | // return new TorusConnector({ 164 | // chainId: 1, 165 | // }); 166 | // }, 167 | // name: "Torus", 168 | // iconName: "torus.png", 169 | // description: "Login using Torus hosted wallet", 170 | // href: null, 171 | // color: "#315CF5", 172 | // mobile: true, 173 | // }, 174 | // Binance: { 175 | // connector: async () => { 176 | // const BscConnector = (await import("@binance-chain/bsc-connector")).BscConnector; 177 | // return new BscConnector({ 178 | // supportedChainIds: [56], 179 | // }); 180 | // }, 181 | // name: "Binance", 182 | // iconName: "bsc.jpg", 183 | // description: "Login using Binance hosted wallet", 184 | // href: null, 185 | // color: "#F0B90B", 186 | // mobile: true, 187 | // }, 188 | // Clover: { 189 | // connector: async () => { 190 | // const CloverConnector = (await import("@clover-network/clover-connector")).CloverConnector; 191 | // return new CloverConnector({ 192 | // supportedChainIds: [1], 193 | // }); 194 | // }, 195 | // name: "Clover", 196 | // iconName: "clover.svg", 197 | // description: "Login using Clover hosted wallet", 198 | // href: null, 199 | // color: "#269964", 200 | // }, 201 | }; 202 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | export const NetworkContextName = "NETWORK"; 2 | -------------------------------------------------------------------------------- /src/config/tokens/bsc.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "WBNB Token", 4 | "symbol": "WBNB", 5 | "address": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", 6 | "chainId": 56, 7 | "decimals": 18, 8 | "logoURI": "https://pancakeswap.finance/images/tokens/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c.png" 9 | }, 10 | { 11 | "name": "BUSD Token", 12 | "symbol": "BUSD", 13 | "address": "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", 14 | "chainId": 56, 15 | "decimals": 18, 16 | "logoURI": "https://pancakeswap.finance/images/tokens/0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56.png" 17 | }, 18 | { 19 | "name": "Ethereum Token", 20 | "symbol": "ETH", 21 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 22 | "chainId": 56, 23 | "decimals": 18, 24 | "logoURI": "https://pancakeswap.finance/images/tokens/0x2170Ed0880ac9A755fd29B2688956BD959F933F8.png" 25 | }, 26 | { 27 | "name": "BTCB Token", 28 | "symbol": "BTCB", 29 | "address": "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c", 30 | "chainId": 56, 31 | "decimals": 18, 32 | "logoURI": "https://pancakeswap.finance/images/tokens/0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c.png" 33 | }, 34 | { 35 | "name": "Tether USD", 36 | "symbol": "USDT", 37 | "address": "0x55d398326f99059fF775485246999027B3197955", 38 | "chainId": 56, 39 | "decimals": 18, 40 | "logoURI": "https://pancakeswap.finance/images/tokens/0x55d398326f99059fF775485246999027B3197955.png" 41 | }, 42 | { 43 | "name": "PancakeSwap Token", 44 | "symbol": "CAKE", 45 | "address": "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82", 46 | "chainId": 56, 47 | "decimals": 18, 48 | "logoURI": "https://pancakeswap.finance/images/tokens/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82.png" 49 | }, 50 | { 51 | "name": "Venus", 52 | "symbol": "XVS", 53 | "address": "0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63", 54 | "chainId": 56, 55 | "decimals": 18, 56 | "logoURI": "https://pancakeswap.finance/images/tokens/0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63.png" 57 | }, 58 | { 59 | "name": "VAI Stablecoin", 60 | "symbol": "VAI", 61 | "address": "0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7", 62 | "chainId": 56, 63 | "decimals": 18, 64 | "logoURI": "https://pancakeswap.finance/images/tokens/0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7.png" 65 | }, 66 | { 67 | "name": "Pancake Bunny", 68 | "symbol": "BUNNY", 69 | "address": "0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51", 70 | "chainId": 56, 71 | "decimals": 18, 72 | "logoURI": "https://pancakeswap.finance/images/tokens/0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51.png" 73 | }, 74 | { 75 | "name": "Alpaca", 76 | "symbol": "ALPACA", 77 | "address": "0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F", 78 | "chainId": 56, 79 | "decimals": 18, 80 | "logoURI": "https://pancakeswap.finance/images/tokens/0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F.png" 81 | }, 82 | { 83 | "name": "Belt", 84 | "symbol": "BELT", 85 | "address": "0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f", 86 | "chainId": 56, 87 | "decimals": 18, 88 | "logoURI": "https://pancakeswap.finance/images/tokens/0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f.png" 89 | }, 90 | { 91 | "name": "TokoCrypto", 92 | "symbol": "TKO", 93 | "address": "0x9f589e3eabe42ebC94A44727b3f3531C0c877809", 94 | "chainId": 56, 95 | "decimals": 18, 96 | "logoURI": "https://pancakeswap.finance/images/tokens/0x9f589e3eabe42ebC94A44727b3f3531C0c877809.png" 97 | }, 98 | { 99 | "name": "Nerve Finance", 100 | "symbol": "NRV", 101 | "address": "0x42F6f551ae042cBe50C739158b4f0CAC0Edb9096", 102 | "chainId": 56, 103 | "decimals": 18, 104 | "logoURI": "https://pancakeswap.finance/images/tokens/0x42F6f551ae042cBe50C739158b4f0CAC0Edb9096.png" 105 | }, 106 | { 107 | "name": "Ellipsis", 108 | "symbol": "EPS", 109 | "address": "0xA7f552078dcC247C2684336020c03648500C6d9F", 110 | "chainId": 56, 111 | "decimals": 18, 112 | "logoURI": "https://pancakeswap.finance/images/tokens/0xA7f552078dcC247C2684336020c03648500C6d9F.png" 113 | } 114 | ] 115 | -------------------------------------------------------------------------------- /src/config/tokens/bsctestnet.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "WBNB Token", 4 | "symbol": "WBNB", 5 | "address": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", 6 | "chainId": 97, 7 | "decimals": 18, 8 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png" 9 | }, 10 | { 11 | "name": "BUSD Token", 12 | "symbol": "BUSD", 13 | "address": "0xeD24FC36d5Ee211Ea25A80239Fb8C4Cfd80f12Ee", 14 | "chainId": 97, 15 | "decimals": 18, 16 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/assets/BUSD-BD1/logo.png" 17 | }, 18 | { 19 | "name": "USDT Token", 20 | "symbol": "USDT", 21 | "address": "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd", 22 | "chainId": 97, 23 | "decimals": 18, 24 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/assets/USDT-6D8/logo.png" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /src/config/tokens/index.ts: -------------------------------------------------------------------------------- 1 | import bsc from "./bsc.json"; 2 | import bsctestnet from "./bsctestnet.json"; 3 | import kovan from "./kovan.json"; 4 | import rinkeby from "./rinkeby.json"; 5 | import ropsten from "./ropsten.json"; 6 | 7 | import { Token } from "@/config/constants/types"; 8 | import { ChainId } from "../constants/chainId"; 9 | 10 | type TokenList = { [chainId: number]: Token[] }; 11 | 12 | const TOKENLIST: TokenList = { 13 | [ChainId.RINKEBY]: rinkeby, 14 | [ChainId.BSC]: bsc, 15 | [ChainId.BSC_TESTNET]: bsctestnet, 16 | [ChainId.KOVAN]: kovan, 17 | [ChainId.ROPSTEN]: ropsten, 18 | }; 19 | 20 | export default TOKENLIST; 21 | -------------------------------------------------------------------------------- /src/config/tokens/kovan.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Wrapped Ether", 4 | "address": "0xd0A1E359811322d97991E03f863a0C30C2cF029C", 5 | "symbol": "WETH", 6 | "decimals": 18, 7 | "chainId": 42, 8 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xd0A1E359811322d97991E03f863a0C30C2cF029C/logo.png" 9 | }, 10 | { 11 | "name": "Maker", 12 | "address": "0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD", 13 | "symbol": "MKR", 14 | "decimals": 18, 15 | "chainId": 42, 16 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD/logo.png" 17 | }, 18 | { 19 | "name": "Uniswap", 20 | "address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", 21 | "symbol": "UNI", 22 | "decimals": 18, 23 | "chainId": 42, 24 | "logoURI": "ipfs://QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" 25 | }, 26 | { 27 | "name": "SushiToken", 28 | "address": "0x0769fd68dFb93167989C6f7254cd0D766Fb2841F", 29 | "symbol": "SUSHI", 30 | "decimals": 18, 31 | "chainId": 42 32 | }, 33 | { 34 | "chainId": 42, 35 | "address": "0x162c44e53097e7B5aaE939b297ffFD6Bf90D1EE3", 36 | "name": "0x Protocol Token", 37 | "symbol": "ZRX", 38 | "decimals": 18, 39 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_ZRX.svg" 40 | }, 41 | { 42 | "chainId": 42, 43 | "address": "0x4a92E71227D294F041BD82dd8f78591B75140d63", 44 | "name": "Compound USD Coin", 45 | "symbol": "cUSDC", 46 | "decimals": 8, 47 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_usdc.svg" 48 | }, 49 | { 50 | "chainId": 42, 51 | "address": "0xF0d0EB522cfa50B716B3b1604C4F0fA6f04376AD", 52 | "name": "Compound Dai", 53 | "symbol": "cDAI", 54 | "decimals": 8, 55 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_dai.svg" 56 | }, 57 | { 58 | "chainId": 42, 59 | "address": "0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa", 60 | "name": "Dai Stablecoin", 61 | "symbol": "DAI", 62 | "decimals": 18, 63 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_DAI.svg" 64 | }, 65 | { 66 | "chainId": 42, 67 | "address": "0xD1308F63823221518Ec88EB209CBaa1ac182105f", 68 | "name": "Sai Stablecoin v1.0", 69 | "symbol": "SAI", 70 | "decimals": 18, 71 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_SAI.svg" 72 | }, 73 | { 74 | "chainId": 42, 75 | "address": "0x07de306FF27a2B630B1141956844eB1552B956B5", 76 | "name": "Tether USD", 77 | "symbol": "USDT", 78 | "decimals": 6, 79 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_USDT.svg" 80 | }, 81 | { 82 | "chainId": 42, 83 | "address": "0x61460874a7196d6a22D1eE4922473664b3E95270", 84 | "name": "Compound", 85 | "symbol": "COMP", 86 | "decimals": 18, 87 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_COMP.svg" 88 | }, 89 | { 90 | "chainId": 42, 91 | "address": "0x3f0A0EA2f86baE6362CF9799B523BA06647Da018", 92 | "name": "Compound USDT", 93 | "symbol": "cUSDT", 94 | "decimals": 8, 95 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_usdt.svg" 96 | }, 97 | { 98 | "chainId": 42, 99 | "address": "0x4a77fAeE9650b09849Ff459eA1476eaB01606C7a", 100 | "name": "Compound Basic Attention Token", 101 | "symbol": "cBAT", 102 | "decimals": 8, 103 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_bat.svg" 104 | }, 105 | { 106 | "chainId": 42, 107 | "address": "0x482dC9bB08111CB875109B075A40881E48aE02Cd", 108 | "name": "Basic Attention Token", 109 | "symbol": "BAT", 110 | "decimals": 18, 111 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_BAT.svg" 112 | }, 113 | { 114 | "chainId": 42, 115 | "address": "0x41B5844f4680a8C38fBb695b7F9CFd1F64474a72", 116 | "name": "Compound Ether", 117 | "symbol": "cETH", 118 | "decimals": 8, 119 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_eth.svg" 120 | }, 121 | { 122 | "chainId": 42, 123 | "address": "0xb3f7fB482492f4220833De6D6bfCC81157214bEC", 124 | "name": "Compound Sai", 125 | "symbol": "cSAI", 126 | "decimals": 8, 127 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_sai.svg" 128 | }, 129 | { 130 | "chainId": 42, 131 | "address": "0xA4eC170599a1Cf87240a35b9B1B8Ff823f448b57", 132 | "name": "Compound Augur", 133 | "symbol": "cREP", 134 | "decimals": 8, 135 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_rep.svg" 136 | }, 137 | { 138 | "chainId": 42, 139 | "address": "0xd3A691C852CDB01E281545A27064741F0B7f6825", 140 | "name": "Wrapped BTC", 141 | "symbol": "WBTC", 142 | "decimals": 8 143 | }, 144 | { 145 | "chainId": 42, 146 | "address": "0x50DD65531676F718B018De3dc48F92B53D756996", 147 | "name": "Reputation", 148 | "symbol": "REP", 149 | "decimals": 18, 150 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_REP.svg" 151 | }, 152 | { 153 | "chainId": 42, 154 | "address": "0xAf45ae737514C8427D373D50Cd979a242eC59e5a", 155 | "name": "Compound 0x", 156 | "symbol": "cZRX", 157 | "decimals": 8, 158 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_zrx.svg" 159 | }, 160 | { 161 | "chainId": 42, 162 | "address": "0xa1fAA15655B0e7b6B6470ED3d096390e6aD93Abb", 163 | "name": "Compound Wrapped BTC", 164 | "symbol": "cWBTC", 165 | "decimals": 8, 166 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/ctoken_wbtc.svg" 167 | }, 168 | { 169 | "chainId": 42, 170 | "address": "0xb7a4F3E9097C08dA09517b5aB877F7a917224ede", 171 | "name": "USD Coin USDC", 172 | "symbol": "USDC", 173 | "decimals": 6, 174 | "logoURI": "https://raw.githubusercontent.com/compound-finance/token-list/master/assets/asset_USDC.svg" 175 | } 176 | ] 177 | -------------------------------------------------------------------------------- /src/config/tokens/rinkeby.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Wrapped Ether", 4 | "address": "0xc778417E063141139Fce010982780140Aa0cD5Ab", 5 | "symbol": "WETH", 6 | "decimals": 18, 7 | "chainId": 4, 8 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc778417E063141139Fce010982780140Aa0cD5Ab/logo.png" 9 | }, 10 | { 11 | "name": "Dai Stablecoin", 12 | "address": "0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735", 13 | "symbol": "DAI", 14 | "decimals": 18, 15 | "chainId": 4, 16 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735/logo.png" 17 | }, 18 | { 19 | "name": "Maker", 20 | "address": "0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85", 21 | "symbol": "MKR", 22 | "decimals": 18, 23 | "chainId": 4, 24 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/logo.png" 25 | }, 26 | { 27 | "name": "Uniswap", 28 | "address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", 29 | "symbol": "UNI", 30 | "decimals": 18, 31 | "chainId": 4, 32 | "logoURI": "ipfs://QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" 33 | }, 34 | { 35 | "name": "SushiToken", 36 | "address": "0x5457Cc9B34eA486eB8d3286329F3536f71fa8A8B", 37 | "symbol": "SUSHI", 38 | "decimals": 18, 39 | "chainId": 4 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /src/config/tokens/ropsten.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Wrapped Ether", 4 | "address": "0xc778417E063141139Fce010982780140Aa0cD5Ab", 5 | "symbol": "WETH", 6 | "decimals": 18, 7 | "chainId": 3, 8 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xc778417E063141139Fce010982780140Aa0cD5Ab/logo.png" 9 | }, 10 | { 11 | "name": "Dai Stablecoin", 12 | "address": "0xc2118d4d90b274016cB7a54c03EF52E6c537D957", 13 | "symbol": "DAI", 14 | "decimals": 18, 15 | "chainId": 3, 16 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xaD6D458402F60fD3Bd25163575031ACDce07538D/logo.png" 17 | }, 18 | { 19 | "name": "Uniswap", 20 | "address": "0x71d82Eb6A5051CfF99582F4CDf2aE9cD402A4882", 21 | "symbol": "UNI", 22 | "decimals": 18, 23 | "chainId": 3, 24 | "logoURI": "ipfs://QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" 25 | }, 26 | { 27 | "name": "SushiToken", 28 | "address": "0x5457Cc9B34eA486eB8d3286329F3536f71fa8A8B", 29 | "symbol": "SUSHI", 30 | "decimals": 18, 31 | "chainId": 3 32 | }, 33 | { 34 | "name": "USD Coin", 35 | "address": "0x0D9C8723B343A8368BebE0B5E89273fF8D712e3C", 36 | "symbol": "USDC", 37 | "decimals": 6, 38 | "chainId": 3, 39 | "logoURI": "https://raw.githubusercontent.com/sushiswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png" 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /src/contracts/MutilTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.7; 4 | 5 | interface IERC20 { 6 | function totalSupply() external view returns (uint256); 7 | 8 | function balanceOf(address account) external view returns (uint256); 9 | 10 | function transfer(address to, uint256 amount) external returns (bool); 11 | 12 | function allowance(address owner, address spender) 13 | external 14 | view 15 | returns (uint256); 16 | 17 | function approve(address spender, uint256 amount) external returns (bool); 18 | 19 | function transferFrom( 20 | address from, 21 | address to, 22 | uint256 amount 23 | ) external returns (bool); 24 | 25 | event Transfer(address indexed from, address indexed to, uint256 value); 26 | 27 | event Approval( 28 | address indexed owner, 29 | address indexed spender, 30 | uint256 value 31 | ); 32 | } 33 | 34 | library Address { 35 | function isContract(address account) internal view returns (bool) { 36 | return account.code.length > 0; 37 | } 38 | 39 | function sendValue(address payable recipient, uint256 amount) internal { 40 | require( 41 | address(this).balance >= amount, 42 | "Address: insufficient balance" 43 | ); 44 | 45 | (bool success, ) = recipient.call{value: amount}(""); 46 | require( 47 | success, 48 | "Address: unable to send value, recipient may have reverted" 49 | ); 50 | } 51 | 52 | function functionCall(address target, bytes memory data) 53 | internal 54 | returns (bytes memory) 55 | { 56 | return functionCall(target, data, "Address: low-level call failed"); 57 | } 58 | 59 | function functionCall( 60 | address target, 61 | bytes memory data, 62 | string memory errorMessage 63 | ) internal returns (bytes memory) { 64 | return functionCallWithValue(target, data, 0, errorMessage); 65 | } 66 | 67 | function functionCallWithValue( 68 | address target, 69 | bytes memory data, 70 | uint256 value 71 | ) internal returns (bytes memory) { 72 | return 73 | functionCallWithValue( 74 | target, 75 | data, 76 | value, 77 | "Address: low-level call with value failed" 78 | ); 79 | } 80 | 81 | function functionCallWithValue( 82 | address target, 83 | bytes memory data, 84 | uint256 value, 85 | string memory errorMessage 86 | ) internal returns (bytes memory) { 87 | require( 88 | address(this).balance >= value, 89 | "Address: insufficient balance for call" 90 | ); 91 | require(isContract(target), "Address: call to non-contract"); 92 | 93 | (bool success, bytes memory returndata) = target.call{value: value}( 94 | data 95 | ); 96 | return verifyCallResult(success, returndata, errorMessage); 97 | } 98 | 99 | function functionStaticCall(address target, bytes memory data) 100 | internal 101 | view 102 | returns (bytes memory) 103 | { 104 | return 105 | functionStaticCall( 106 | target, 107 | data, 108 | "Address: low-level static call failed" 109 | ); 110 | } 111 | 112 | function functionStaticCall( 113 | address target, 114 | bytes memory data, 115 | string memory errorMessage 116 | ) internal view returns (bytes memory) { 117 | require(isContract(target), "Address: static call to non-contract"); 118 | 119 | (bool success, bytes memory returndata) = target.staticcall(data); 120 | return verifyCallResult(success, returndata, errorMessage); 121 | } 122 | 123 | function functionDelegateCall(address target, bytes memory data) 124 | internal 125 | returns (bytes memory) 126 | { 127 | return 128 | functionDelegateCall( 129 | target, 130 | data, 131 | "Address: low-level delegate call failed" 132 | ); 133 | } 134 | 135 | function functionDelegateCall( 136 | address target, 137 | bytes memory data, 138 | string memory errorMessage 139 | ) internal returns (bytes memory) { 140 | require(isContract(target), "Address: delegate call to non-contract"); 141 | 142 | (bool success, bytes memory returndata) = target.delegatecall(data); 143 | return verifyCallResult(success, returndata, errorMessage); 144 | } 145 | 146 | function verifyCallResult( 147 | bool success, 148 | bytes memory returndata, 149 | string memory errorMessage 150 | ) internal pure returns (bytes memory) { 151 | if (success) { 152 | return returndata; 153 | } else { 154 | if (returndata.length > 0) { 155 | assembly { 156 | let returndata_size := mload(returndata) 157 | revert(add(32, returndata), returndata_size) 158 | } 159 | } else { 160 | revert(errorMessage); 161 | } 162 | } 163 | } 164 | } 165 | 166 | library SafeERC20 { 167 | using Address for address; 168 | 169 | function safeTransfer( 170 | IERC20 token, 171 | address to, 172 | uint256 value 173 | ) internal { 174 | _callOptionalReturn( 175 | token, 176 | abi.encodeWithSelector(token.transfer.selector, to, value) 177 | ); 178 | } 179 | 180 | function safeTransferFrom( 181 | IERC20 token, 182 | address from, 183 | address to, 184 | uint256 value 185 | ) internal { 186 | _callOptionalReturn( 187 | token, 188 | abi.encodeWithSelector(token.transferFrom.selector, from, to, value) 189 | ); 190 | } 191 | 192 | function safeApprove( 193 | IERC20 token, 194 | address spender, 195 | uint256 value 196 | ) internal { 197 | require( 198 | (value == 0) || (token.allowance(address(this), spender) == 0), 199 | "SafeERC20: approve from non-zero to non-zero allowance" 200 | ); 201 | _callOptionalReturn( 202 | token, 203 | abi.encodeWithSelector(token.approve.selector, spender, value) 204 | ); 205 | } 206 | 207 | function safeIncreaseAllowance( 208 | IERC20 token, 209 | address spender, 210 | uint256 value 211 | ) internal { 212 | uint256 newAllowance = token.allowance(address(this), spender) + value; 213 | _callOptionalReturn( 214 | token, 215 | abi.encodeWithSelector( 216 | token.approve.selector, 217 | spender, 218 | newAllowance 219 | ) 220 | ); 221 | } 222 | 223 | function safeDecreaseAllowance( 224 | IERC20 token, 225 | address spender, 226 | uint256 value 227 | ) internal { 228 | unchecked { 229 | uint256 oldAllowance = token.allowance(address(this), spender); 230 | require( 231 | oldAllowance >= value, 232 | "SafeERC20: decreased allowance below zero" 233 | ); 234 | uint256 newAllowance = oldAllowance - value; 235 | _callOptionalReturn( 236 | token, 237 | abi.encodeWithSelector( 238 | token.approve.selector, 239 | spender, 240 | newAllowance 241 | ) 242 | ); 243 | } 244 | } 245 | 246 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 247 | bytes memory returndata = address(token).functionCall( 248 | data, 249 | "SafeERC20: low-level call failed" 250 | ); 251 | if (returndata.length > 0) { 252 | require( 253 | abi.decode(returndata, (bool)), 254 | "SafeERC20: ERC20 operation did not succeed" 255 | ); 256 | } 257 | } 258 | } 259 | 260 | abstract contract Context { 261 | function _msgSender() internal view virtual returns (address) { 262 | return msg.sender; 263 | } 264 | 265 | function _msgData() internal view virtual returns (bytes calldata) { 266 | return msg.data; 267 | } 268 | } 269 | 270 | abstract contract Ownable is Context { 271 | address private _owner; 272 | 273 | event OwnershipTransferred( 274 | address indexed previousOwner, 275 | address indexed newOwner 276 | ); 277 | 278 | constructor() { 279 | _transferOwnership(_msgSender()); 280 | } 281 | 282 | function owner() public view virtual returns (address) { 283 | return _owner; 284 | } 285 | 286 | modifier onlyOwner() { 287 | require(owner() == _msgSender(), "Ownable: caller is not the owner"); 288 | _; 289 | } 290 | 291 | function renounceOwnership() public virtual onlyOwner { 292 | _transferOwnership(address(0)); 293 | } 294 | 295 | function transferOwnership(address newOwner) public virtual onlyOwner { 296 | require( 297 | newOwner != address(0), 298 | "Ownable: new owner is the zero address" 299 | ); 300 | _transferOwnership(newOwner); 301 | } 302 | 303 | function _transferOwnership(address newOwner) internal virtual { 304 | address oldOwner = _owner; 305 | _owner = newOwner; 306 | emit OwnershipTransferred(oldOwner, newOwner); 307 | } 308 | } 309 | 310 | library SafeMath { 311 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 312 | uint256 c = a + b; 313 | require(c >= a, "SafeMath: addition overflow"); 314 | 315 | return c; 316 | } 317 | 318 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 319 | return sub(a, b, "SafeMath: subtraction overflow"); 320 | } 321 | 322 | function sub( 323 | uint256 a, 324 | uint256 b, 325 | string memory errorMessage 326 | ) internal pure returns (uint256) { 327 | require(b <= a, errorMessage); 328 | uint256 c = a - b; 329 | 330 | return c; 331 | } 332 | 333 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 334 | if (a == 0) { 335 | return 0; 336 | } 337 | 338 | uint256 c = a * b; 339 | require(c / a == b, "SafeMath: multiplication overflow"); 340 | 341 | return c; 342 | } 343 | 344 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 345 | return div(a, b, "SafeMath: division by zero"); 346 | } 347 | 348 | function div( 349 | uint256 a, 350 | uint256 b, 351 | string memory errorMessage 352 | ) internal pure returns (uint256) { 353 | require(b > 0, errorMessage); 354 | uint256 c = a / b; 355 | 356 | return c; 357 | } 358 | 359 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 360 | return mod(a, b, "SafeMath: modulo by zero"); 361 | } 362 | 363 | function mod( 364 | uint256 a, 365 | uint256 b, 366 | string memory errorMessage 367 | ) internal pure returns (uint256) { 368 | require(b != 0, errorMessage); 369 | return a % b; 370 | } 371 | } 372 | 373 | contract MutilTransfer is Ownable { 374 | using SafeMath for uint256; 375 | using SafeERC20 for IERC20; 376 | uint256 public fee = 0; 377 | 378 | function transferETH(address payable[] memory to, uint256 amount) 379 | public 380 | payable 381 | { 382 | uint256 length = to.length; 383 | require( 384 | msg.value == length.mul(amount).add(fee), 385 | "Transfer amount error" 386 | ); 387 | 388 | payable(owner()).transfer(fee); 389 | for (uint256 i = 0; i < length; i++) { 390 | to[i].transfer(amount); 391 | } 392 | } 393 | 394 | function transferMultiETH( 395 | address payable[] memory to, 396 | uint256[] memory amount 397 | ) public payable { 398 | uint256 length = to.length; 399 | require(to.length == amount.length, "Transfer length error"); 400 | uint256 allAmount; 401 | for (uint256 i = 0; i < length; i++) { 402 | allAmount += amount[i]; 403 | } 404 | require(msg.value == allAmount.add(fee), "Transfer amount error"); 405 | payable(owner()).transfer(fee); 406 | for (uint256 i = 0; i < length; i++) { 407 | to[i].transfer(amount[i]); 408 | } 409 | } 410 | 411 | function transferToken( 412 | address _token, 413 | address[] memory to, 414 | uint256 amount 415 | ) public payable { 416 | IERC20 token = IERC20(_token); 417 | require(fee == msg.value, "Transfer amount error"); 418 | payable(owner()).transfer(fee); 419 | uint256 length = to.length; 420 | for (uint256 i = 0; i < length; i++) { 421 | token.safeTransferFrom(msg.sender, to[i], amount); 422 | } 423 | } 424 | 425 | function transferMultiToken( 426 | address _token, 427 | address[] memory to, 428 | uint256[] memory amount 429 | ) public payable { 430 | IERC20 token = IERC20(_token); 431 | require(fee == msg.value, "Transfer amount error"); 432 | require(to.length == amount.length, "Transfer length error"); 433 | payable(owner()).transfer(fee); 434 | uint256 length = to.length; 435 | for (uint256 i = 0; i < length; i++) { 436 | token.safeTransferFrom(msg.sender, to[i], amount[i]); 437 | } 438 | } 439 | 440 | function setFee(uint256 _fee) public onlyOwner { 441 | fee = _fee; 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/hooks/useActiveWeb3React.ts: -------------------------------------------------------------------------------- 1 | import { NetworkContextName } from "@/config/index"; 2 | import { Web3Provider } from "@ethersproject/providers"; 3 | import { useWeb3React } from "@web3-react/core"; 4 | import { Web3ReactContextInterface } from "@web3-react/core/dist/types"; 5 | 6 | export function useActiveWeb3React(): Web3ReactContextInterface { 7 | const context = useWeb3React(); 8 | const contextNetwork = useWeb3React(NetworkContextName); 9 | 10 | return context.active ? { ...context } : { ...contextNetwork }; 11 | } 12 | -------------------------------------------------------------------------------- /src/hooks/useContract.ts: -------------------------------------------------------------------------------- 1 | import { useActiveWeb3React } from "@/hooks/useActiveWeb3React"; 2 | import { getProviderOrSigner } from "@/utils"; 3 | import { getMultiTransferAddress } from "@/utils/contractAddressHelper"; 4 | import { isAddress } from "@ethersproject/address"; 5 | import { AddressZero } from "@ethersproject/constants"; 6 | import { Contract } from "@ethersproject/contracts"; 7 | import { Web3Provider } from "@ethersproject/providers"; 8 | import { useMemo } from "react"; 9 | 10 | import ERC20_ABI from "@/config/abi/erc20.json"; 11 | import Transfer_ABI from "@/config/abi/MutilTransfer.json"; 12 | // export const useExampleContract = (address: string, withSignerIfPossible = true) => { 13 | // return useContract(address, ContractAbi, withSignerIfPossible); 14 | // }; 15 | 16 | // Multiple chains 17 | // export const useBatchTransfer = (withSignerIfPossible?: boolean) => { 18 | // const { chainId } = useActiveWeb3React(); 19 | // return useContract(getContractAddress(chainId), ContractAbi, withSignerIfPossible); 20 | // }; 21 | 22 | export const useTransfer = (withSignerIfPossible = true) => { 23 | const { chainId } = useActiveWeb3React(); 24 | return useContract(chainId ? getMultiTransferAddress(chainId) : undefined, Transfer_ABI, withSignerIfPossible); 25 | }; 26 | 27 | export const useERC20 = (address: string, withSignerIfPossible = true) => { 28 | return useContract(address, ERC20_ABI, withSignerIfPossible); 29 | }; 30 | 31 | export function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null { 32 | const { library, account } = useActiveWeb3React(); 33 | return useMemo(() => { 34 | if (!address || address === AddressZero || !ABI || !library) return null; 35 | try { 36 | return getContract(address, ABI, library, withSignerIfPossible && account ? account : undefined); 37 | } catch (error) { 38 | console.error("Failed to get contract", error); 39 | return null; 40 | } 41 | }, [address, ABI, library, withSignerIfPossible, account]); 42 | } 43 | 44 | export function getContract(address: string, ABI: any, library: Web3Provider, account?: string): Contract { 45 | if (!isAddress(address) || address === AddressZero) { 46 | throw Error(`Invalid 'address' parameter '${address}'.`); 47 | } 48 | return new Contract(address, ABI, getProviderOrSigner(library, account)); 49 | } 50 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export default function useDebounce(value: T, delay: number): T { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | // Update debounced value after delay 8 | const handler = setTimeout(() => { 9 | setDebouncedValue(value); 10 | }, delay); 11 | 12 | return () => { 13 | clearTimeout(handler); 14 | }; 15 | }, [value, delay]); 16 | 17 | return debouncedValue; 18 | } 19 | -------------------------------------------------------------------------------- /src/hooks/useEagerConnect.ts: -------------------------------------------------------------------------------- 1 | import { connectorLocalStorageKey } from "@/config/connectors/index"; 2 | import { injected } from "@/config/constants/wallets"; 3 | import { useWeb3React as useWeb3ReactCore } from "@web3-react/core"; 4 | import { useEffect, useState } from "react"; 5 | import { isMobile } from "web3modal"; 6 | export function useEagerConnect() { 7 | const { activate, active } = useWeb3ReactCore(); // specifically using useWeb3ReactCore because of what this hook does 8 | const [tried, setTried] = useState(false); 9 | 10 | useEffect(() => { 11 | injected.isAuthorized().then((isAuthorized: any) => { 12 | const hasSignedIn = window.localStorage.getItem(connectorLocalStorageKey); 13 | 14 | if (isAuthorized && hasSignedIn) { 15 | activate(injected, undefined, true) 16 | // .then(() => window.ethereum.removeAllListeners(['networkChanged'])) 17 | .catch(() => { 18 | setTried(true); 19 | }); 20 | // @ts-ignore TYPE NEEDS FIXING 21 | window.ethereum.removeAllListeners(["networkChanged"]); 22 | } else { 23 | if (isMobile() && window.ethereum && hasSignedIn) { 24 | activate(injected, undefined, true) 25 | // .then(() => window.ethereum.removeAllListeners(['networkChanged'])) 26 | .catch(() => { 27 | setTried(true); 28 | }); 29 | // @ts-ignore TYPE NEEDS FIXING 30 | window.ethereum.removeAllListeners(["networkChanged"]); 31 | } else { 32 | setTried(true); 33 | } 34 | } 35 | }); 36 | }, [activate]); 37 | 38 | useEffect(() => { 39 | if (active) { 40 | setTried(true); 41 | } 42 | }, [active]); 43 | 44 | return tried; 45 | } 46 | 47 | export default useEagerConnect; 48 | -------------------------------------------------------------------------------- /src/hooks/useInactiveListener.ts: -------------------------------------------------------------------------------- 1 | import { useWeb3React as useWeb3ReactCore } from "@web3-react/core"; 2 | import { useEffect } from "react"; 3 | 4 | import { injected } from "@/config/constants/wallets"; 5 | 6 | /** 7 | * Use for network and injected - logs user in 8 | * and out after checking what network theyre on 9 | */ 10 | function useInactiveListener(suppress = false) { 11 | const { active, error, activate } = useWeb3ReactCore(); // specifically using useWeb3React because of what this hook does 12 | 13 | useEffect(() => { 14 | const { ethereum } = window; 15 | 16 | if (ethereum && ethereum.on && !active && !error && !suppress) { 17 | const handleChainChanged = () => { 18 | // eat errors 19 | activate(injected, undefined, true).catch((error) => { 20 | console.error("Failed to activate after chain changed", error); 21 | }); 22 | }; 23 | 24 | const handleAccountsChanged = (accounts: string[]) => { 25 | if (accounts.length > 0) { 26 | // eat errors 27 | activate(injected, undefined, true).catch((error) => { 28 | console.error("Failed to activate after accounts changed", error); 29 | }); 30 | } 31 | }; 32 | 33 | ethereum.on("chainChanged", handleChainChanged); 34 | ethereum.on("accountsChanged", handleAccountsChanged); 35 | 36 | return () => { 37 | if (ethereum.removeListener) { 38 | ethereum.removeListener("chainChanged", handleChainChanged); 39 | ethereum.removeListener("accountsChanged", handleAccountsChanged); 40 | } 41 | }; 42 | } 43 | return undefined; 44 | }, [active, error, suppress, activate]); 45 | } 46 | 47 | export default useInactiveListener; 48 | -------------------------------------------------------------------------------- /src/hooks/useTransfer.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "@/config/constants/types"; 2 | import { useTransfer } from "@/hooks/useContract"; 3 | import { accAdd, accGt, accMul, parseAmount } from "@/utils/format"; 4 | import { isEth } from "@/utils/isEth"; 5 | import { useEffect, useState } from "react"; 6 | import { useActiveWeb3React } from "./useActiveWeb3React"; 7 | import { useERC20 } from "./useContract"; 8 | export const useAllowance = (token: Token, account: string, to: string) => { 9 | const { chainId } = useActiveWeb3React(); 10 | 11 | const [isApproved, setIsApproved] = useState(false); 12 | const bep20Contract = useERC20(token.address); 13 | 14 | const getAllowance = async () => { 15 | if (!account) { 16 | return; 17 | } 18 | if (isEth(token, chainId)) { 19 | setIsApproved(true); 20 | return; 21 | } 22 | const response = await bep20Contract.allowance(account, to); 23 | setIsApproved(accGt(response.toString(), "0")); 24 | }; 25 | useEffect(() => { 26 | getAllowance(); 27 | }, [account]); 28 | 29 | return { isApproved, getAllowance }; 30 | }; 31 | export const useTransferFee = () => { 32 | const [fee, setFee] = useState(""); 33 | const TransferInstance = useTransfer(); 34 | 35 | const getTransferFee = async () => { 36 | const response = await TransferInstance.fee(); 37 | 38 | setFee(response.toString()); 39 | }; 40 | 41 | useEffect(() => { 42 | getTransferFee(); 43 | }, []); 44 | 45 | return { fee }; 46 | }; 47 | interface TransferGasFee { 48 | token: Token; 49 | isApproved: boolean; 50 | amount?: string; 51 | toAddressList: string[]; 52 | allAmount: string; 53 | fee: string; 54 | tokenAmountList?: string[]; 55 | } 56 | export const useTransferGasFee = ({ token, isApproved, amount, toAddressList, allAmount, fee }: TransferGasFee) => { 57 | const TransferInstance = useTransfer(); 58 | 59 | const { account, chainId, library } = useActiveWeb3React(); 60 | const [allFee, setAllFee] = useState("0"); 61 | const [errorMessage, setErrorMessage] = useState(""); 62 | const getTransferFee = async () => { 63 | if (!account) { 64 | return; 65 | } 66 | if (!isApproved) { 67 | return; 68 | } 69 | if (fee === "") { 70 | return; 71 | } 72 | try { 73 | const gasPrice = await library.getGasPrice(); 74 | 75 | const tokenAmount = parseAmount(amount, token.decimals); 76 | 77 | if (isEth(token, chainId)) { 78 | console.log(toAddressList); 79 | console.log(tokenAmount); 80 | 81 | const estimateGas = await TransferInstance.estimateGas.transferETH(toAddressList, tokenAmount, { 82 | value: accAdd(allAmount, fee), 83 | }); 84 | 85 | const allFee = accAdd(accMul(gasPrice.toString(), estimateGas.toString()), fee); 86 | 87 | setAllFee(allFee); 88 | } else { 89 | const gasRes = await TransferInstance.estimateGas.transferToken( 90 | token.address, 91 | toAddressList, 92 | tokenAmount, 93 | { 94 | value: fee, 95 | }, 96 | ); 97 | const allFee = accAdd(accMul(gasPrice.toString(), gasRes.toString()), fee); 98 | setAllFee(allFee); 99 | } 100 | setErrorMessage(""); 101 | } catch (callError: any) { 102 | setAllFee(fee); 103 | setErrorMessage( 104 | callError.error?.message || callError.reason || callError.data?.message || callError.message, 105 | ); 106 | } 107 | }; 108 | 109 | useEffect(() => { 110 | getTransferFee(); 111 | }, [account, isApproved, fee, toAddressList]); 112 | 113 | return { allFee, errorMessage }; 114 | }; 115 | 116 | export const useBalance = (token: Token, account: string) => { 117 | const { library, chainId } = useActiveWeb3React(); 118 | const [nativeBalance, setBalance] = useState("0"); 119 | const [tokenBalance, setTokenBalance] = useState("0"); 120 | const ERC20Contract = useERC20(token.address); 121 | 122 | const getBalance = async () => { 123 | if (!account) { 124 | return; 125 | } 126 | const balance = await library.getBalance(account); 127 | setBalance(balance.toString()); 128 | 129 | if (isEth(token, chainId)) { 130 | const tokenBalance = balance; 131 | setTokenBalance(tokenBalance.toString()); 132 | } else { 133 | const tokenBalance = await ERC20Contract.balanceOf(account); 134 | setTokenBalance(tokenBalance.toString()); 135 | } 136 | }; 137 | useEffect(() => { 138 | getBalance(); 139 | }, [account]); 140 | 141 | return { nativeBalance, tokenBalance, getBalance }; 142 | }; 143 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | /* @tailwind base; */ 7 | @tailwind components; 8 | @tailwind utilities; 9 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /src/utils/contractAddressHelper.ts: -------------------------------------------------------------------------------- 1 | import addresses from "@/config/constants/contractAddresses"; 2 | import { Address } from "@/config/constants/types"; 3 | import defaultChainId from "@/config/constants/defaultChainId"; 4 | 5 | export const getAddress = (address: Address, chainId: number): string => { 6 | return address[chainId] ? address[chainId] : address[defaultChainId]; 7 | }; 8 | 9 | export const getMultiTransferAddress = (chainId: number) => { 10 | return getAddress(addresses.multiTransfer, chainId); 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/format.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from "@ethersproject/bignumber"; 2 | import { formatUnits } from "@ethersproject/units"; 3 | import BigNumber from "bignumber.js"; 4 | 5 | export function formatAddress(str: string) { 6 | return str.slice(0, 8) + "..." + str.slice(-5); 7 | } 8 | export const formatBalance = (value: BigNumberish, decimals = 18, maxFraction = 0) => { 9 | const formatted = formatUnits(value, decimals); 10 | if (maxFraction > 0) { 11 | const split = formatted.split("."); 12 | if (split.length > 1) { 13 | return split[0] + "." + split[1].substring(0, maxFraction); 14 | } 15 | } 16 | return formatted; 17 | }; 18 | 19 | export function parseAmount(amount: string, decimal = 18) { 20 | if (!amount) return "0"; 21 | 22 | amount = cleanupAmount(amount); 23 | 24 | const split = amount.split("."); 25 | const wholePart = split[0]; 26 | const fracPart = split[1] || ""; 27 | if (split.length > 2 || fracPart.length > decimal) { 28 | throw new Error(`Cannot parse '${amount}' as bignumber`); 29 | } 30 | return trimLeadingZeroes(wholePart + fracPart.padEnd(decimal, "0")); 31 | } 32 | export function formatAmount(amount: string, decimal = 18) { 33 | if (!amount) return "0"; 34 | 35 | const amountBN = new BigNumber(amount, 10); 36 | amount = amountBN.toString(10); 37 | const wholeStr = amount.substring(0, amount.length - decimal) || "0"; 38 | const fractionStr = amount 39 | .substring(amount.length - decimal) 40 | .padStart(decimal, "0") 41 | .substring(0, decimal); 42 | 43 | return trimTrailingZeroes(`${wholeStr}.${fractionStr}`); 44 | } 45 | 46 | export function cleanupAmount(amount) { 47 | return amount.replace(/,/g, "").trim(); 48 | } 49 | 50 | export function trimTrailingZeroes(value) { 51 | return value.replace(/\.?0*$/, ""); 52 | } 53 | 54 | export function trimLeadingZeroes(value) { 55 | value = value.replace(/^0+/, ""); 56 | if (value === "") { 57 | return "0"; 58 | } 59 | return value; 60 | } 61 | 62 | export function accAdd(arg1: string | number, arg2: string | number) { 63 | const num = new BigNumber(arg1).plus(new BigNumber(arg2)); 64 | 65 | return num.toFixed(); 66 | } 67 | 68 | export function accSub(arg1: string | number, arg2: string | number) { 69 | const num = new BigNumber(arg1).minus(new BigNumber(arg2)); 70 | return num.toFixed(); 71 | } 72 | export function accMul(arg1: string | number, arg2: string | number) { 73 | if (!arg1 || !arg2) { 74 | return "0"; 75 | } 76 | const num = new BigNumber(arg1).times(new BigNumber(arg2)); 77 | return num.toString(); 78 | } 79 | 80 | export function accDiv(arg1: string | number, arg2: string | number) { 81 | if (!arg1 || !arg2) { 82 | return 0; 83 | } 84 | const num = new BigNumber(arg1).div(new BigNumber(arg2)); 85 | return num.toFixed(); 86 | } 87 | 88 | //Greater than 89 | export function accGt(arg1: string, arg2: string) { 90 | return new BigNumber(arg1).gt(new BigNumber(arg2)); 91 | } 92 | 93 | // Greater than or equal to 94 | export function accGte(arg1: string, arg2: string) { 95 | return new BigNumber(arg1).gte(new BigNumber(arg2)); 96 | } 97 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcSigner, Web3Provider } from "@ethersproject/providers"; 2 | 3 | // account is not optional 4 | export function getSigner(library: Web3Provider, account: string): JsonRpcSigner { 5 | return library.getSigner(account).connectUnchecked(); 6 | } 7 | 8 | // account is optional 9 | export function getProviderOrSigner(library: Web3Provider, account?: string): Web3Provider | JsonRpcSigner { 10 | return account ? getSigner(library, account) : library; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/isAddress.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address"; 2 | 3 | export function isAddress(value: any): string | false { 4 | try { 5 | return getAddress(value); 6 | } catch { 7 | return false; 8 | } 9 | } 10 | 11 | export function isAllAddress(value: any): boolean { 12 | if (value.match(/^(0x)?[0-9a-fA-F]{40}$/)) { 13 | return true; 14 | } 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/isEth.ts: -------------------------------------------------------------------------------- 1 | import { NATIVE } from "@/config/constants/native"; 2 | import { Token } from "@/config/constants/types"; 3 | export const isEth = (token: Token, chainId: number) => { 4 | const native = NATIVE[chainId]; 5 | if (native && token.address === native.address && token.symbol.toLowerCase() === native.symbol.toLowerCase()) { 6 | return true; 7 | } 8 | return false; 9 | }; 10 | -------------------------------------------------------------------------------- /src/view/Home/AddressList.tsx: -------------------------------------------------------------------------------- 1 | import TextField from "@mui/material/TextField"; 2 | 3 | interface propsInter { 4 | addressValue: string; 5 | onSetAddressChange: (value: string) => void; 6 | onSetAddressListChange: (value: Array) => void; 7 | addressList: Array; 8 | } 9 | export default function AddressList(props: propsInter) { 10 | const { onSetAddressChange, addressValue, onSetAddressListChange, addressList } = props; 11 | return ( 12 |
13 |
收币地址
14 |
15 |
16 | {addressList.length 17 | ? addressList.map((item, index) => { 18 | return
{index + 1}
; 19 | }) 20 | : 1} 21 |
22 |
23 | { 30 | onSetAddressChange(e.target.value); 31 | if (e.target.value == "") { 32 | onSetAddressListChange([]); 33 | return; 34 | } 35 | const addressList = e.target.value.split("\n"); 36 | onSetAddressListChange(addressList); 37 | }} 38 | InputProps={{ 39 | disableUnderline: true, 40 | }} 41 | sx={{ 42 | "& .MuiInputBase-root": { 43 | padding: 0, 44 | lineHeight: "25px", 45 | fontSize: "15px", 46 | color: "#001A6B", 47 | }, 48 | }} 49 | className="w-full bg-[#F9F9F9] text-clip" 50 | /> 51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/view/Home/ConfirmPage.tsx: -------------------------------------------------------------------------------- 1 | import backIcon from "@/assets/back.svg"; 2 | import nodataIcon from "@/assets/nodata.svg"; 3 | import { NATIVE } from "@/config/constants/native"; 4 | import { Token } from "@/config/constants/types"; 5 | import { useActiveWeb3React } from "@/hooks/useActiveWeb3React"; 6 | import { useERC20, useTransfer } from "@/hooks/useContract"; 7 | import { useAllowance, useBalance, useTransferFee, useTransferGasFee } from "@/hooks/useTransfer"; 8 | import { getMultiTransferAddress } from "@/utils/contractAddressHelper"; 9 | import { accAdd, accMul, formatAmount, formatBalance, parseAmount } from "@/utils/format"; 10 | import { isEth } from "@/utils/isEth"; 11 | import { MaxUint256 } from "@ethersproject/constants"; 12 | import LoadingButton from "@mui/lab/LoadingButton"; 13 | import Button from "@mui/material/Button"; 14 | import Table from "@mui/material/Table"; 15 | import TableBody from "@mui/material/TableBody"; 16 | import TableCell from "@mui/material/TableCell"; 17 | import TableHead from "@mui/material/TableHead"; 18 | import TableRow from "@mui/material/TableRow"; 19 | import { useContext, useEffect, useState } from "react"; 20 | import { toast, ToastContainer } from "react-toastify"; 21 | import { Context } from "./index"; 22 | interface ConfirmProps { 23 | addressList: Array; 24 | tableData: Array<{ address: string; amount: number; id: number }>; 25 | delAddressList: (id: number) => void; 26 | sendValue: string; 27 | token: Token; 28 | tokenList: Array; 29 | } 30 | export default function ConfirmPage(props: ConfirmProps) { 31 | const { addressList, delAddressList, sendValue, token, tokenList } = props; 32 | let { confirm, setConfirm } = useContext(Context); 33 | const ERC20Instarnce = useERC20(token.address); 34 | const { chainId, library, account } = useActiveWeb3React(); 35 | const { nativeBalance, tokenBalance, getBalance } = useBalance(token, account); 36 | 37 | const { fee } = useTransferFee(); 38 | const { isApproved, getAllowance } = useAllowance(token, account, getMultiTransferAddress(chainId)); 39 | const { allFee, errorMessage } = useTransferGasFee({ 40 | token, 41 | isApproved, 42 | amount: sendValue, 43 | toAddressList: addressList, 44 | allAmount: accMul(parseAmount(sendValue, token.decimals), addressList.length), 45 | fee, 46 | }); 47 | 48 | const TransferInstance = useTransfer(); 49 | const [tableData, setTableData] = useState([]); 50 | const [loading, setLoading] = useState(false); 51 | 52 | const initArray = () => { 53 | let arr = addressList; 54 | let newArr = []; 55 | for (let i = 0; i < arr.length; i++) { 56 | newArr.push({ 57 | id: i + 1, 58 | address: arr[i], 59 | amount: sendValue, 60 | }); 61 | } 62 | setTableData(newArr); 63 | }; 64 | 65 | const handleTransfer = async () => { 66 | try { 67 | setLoading(true); 68 | 69 | const tokenAmount = parseAmount(sendValue, token.decimals); 70 | const allAmount = accMul(parseAmount(sendValue, token.decimals), addressList.length); 71 | let tx; 72 | 73 | if (isEth(token, chainId)) { 74 | tx = await TransferInstance.transferETH(addressList, tokenAmount.toString(), { 75 | value: accAdd(allAmount, fee), 76 | }); 77 | } else { 78 | tx = await TransferInstance.transferToken(token.address, addressList, tokenAmount.toString(), { 79 | value: fee, 80 | }); 81 | } 82 | await tx.wait(); 83 | setLoading(false); 84 | toast.success("Transfer Success", { 85 | position: toast.POSITION.TOP_LEFT, 86 | theme: "colored", 87 | }); 88 | getBalance(); 89 | } catch (error) { 90 | toast.error(error.reason || error.data?.message || error.message, { 91 | position: toast.POSITION.TOP_LEFT, 92 | theme: "colored", 93 | }); 94 | setLoading(false); 95 | } 96 | }; 97 | const handleApproval = async () => { 98 | setLoading(true); 99 | 100 | try { 101 | const tx = await ERC20Instarnce.approve(getMultiTransferAddress(chainId), MaxUint256); 102 | await tx.wait(); 103 | setLoading(false); 104 | toast.success("Approve Success", { 105 | position: toast.POSITION.TOP_LEFT, 106 | theme: "colored", 107 | }); 108 | getAllowance(); 109 | getBalance(); 110 | } catch (error) { 111 | toast.error(error.reason || error.data?.message || error.message, { 112 | position: toast.POSITION.TOP_LEFT, 113 | theme: "colored", 114 | }); 115 | setLoading(false); 116 | } 117 | }; 118 | useEffect(() => { 119 | initArray(); 120 | }, [addressList]); 121 | 122 | return ( 123 |
124 |
确认交易
125 | {/*
交易速度
*/} 126 | 127 |
地址列表
128 |
129 | 135 | 136 | 137 | 145 | 序号  146 | 147 | 155 | 钱包地址  156 | 157 | 164 | 数量  165 | 166 | 173 | 操作  174 | 175 | 176 | 177 | 178 | {tableData.map( 179 | ( 180 | row: { 181 | id: number; 182 | address: string; 183 | amount: number; 184 | }, 185 | index: number, 186 | ) => ( 187 | 188 | 196 | {row.id} 197 | 198 | 207 | {row.address} 208 | 209 | 215 | {row.amount} 216 | 217 | 224 |
225 | 239 |
240 |
241 |
242 | ), 243 | )} 244 |
245 |
246 | {addressList.length === 0 && ( 247 |
248 | no data 249 |
250 | )} 251 |
252 |
摘要
253 |
254 |
255 |
256 |
257 |
258 | {addressList.length} 259 |
260 |
261 | 地址总数 262 |
263 |
264 | 265 |
266 |
267 | {Number(sendValue) * addressList.length} 268 |
269 |
270 | 代币发送总数 271 |
272 |
273 |
274 | 275 |
276 |
277 |
278 | 1 279 |
280 |
281 | 交易总数 282 |
283 |
284 | 285 |
286 |
287 | {formatBalance(tokenBalance, token.decimals, 3)} {token.symbol} 288 |
289 |
290 | 代币余额 291 |
292 |
293 |
294 |
295 |
296 |
297 | {formatAmount(allFee)} 298 |
299 |
300 | 预估消耗 301 | 302 | (含手续费 {formatAmount(fee)} {NATIVE[chainId].symbol}) 303 | 304 |
305 |
306 | 307 |
308 |
309 | {formatBalance(nativeBalance, 18, 3)} {tokenList[0].symbol} 310 |
311 |
312 | 您的余额 313 |
314 |
315 |
316 |
317 |
318 | 319 | {errorMessage && ( 320 |
321 | {errorMessage} 322 |
323 | )} 324 | 325 |
326 | 335 |
336 | {isApproved ? ( 337 | { 343 | handleTransfer(); 344 | }} 345 | > 346 | 发送 347 | 348 | ) : ( 349 | { 355 | // setConfirm(true); 356 | handleApproval(); 357 | }} 358 | > 359 | 授权 360 | 361 | )} 362 |
363 |
364 | 365 |
366 | ); 367 | } 368 | -------------------------------------------------------------------------------- /src/view/Home/SelectToken.tsx: -------------------------------------------------------------------------------- 1 | import { Token } from "@/config/constants/types"; 2 | import Autocomplete from "@mui/material/Autocomplete"; 3 | import CircularProgress from "@mui/material/CircularProgress"; 4 | import TextField from "@mui/material/TextField"; 5 | import React, { useEffect, useState } from "react"; 6 | 7 | interface propsInter { 8 | tokenList: Array; 9 | onInChange: (value: any) => void; 10 | onEventChange: (value: any) => void; 11 | open: boolean; 12 | token: Token; 13 | } 14 | 15 | export default function SelectToken(props: propsInter) { 16 | const { tokenList, onInChange, onEventChange, open, token } = props; 17 | const [options, setOptions] = useState([]); 18 | const loading = open && options.length === 0; 19 | useEffect(() => { 20 | if (!open) { 21 | setOptions([]); 22 | } 23 | }, [open]); 24 | 25 | return ( 26 |
27 |
选择代币或输入代币地址查询
28 | option.symbol + " " + option.address} 37 | isOptionEqualToValue={(option, newValue) => { 38 | return option.address === newValue.address; 39 | }} 40 | onInputChange={(event, newInputValue) => { 41 | onInChange(newInputValue); 42 | }} 43 | onChange={(event, newValue) => { 44 | onEventChange(newValue); 45 | }} 46 | renderOption={(props, option) => ( 47 |
  • 48 | {option.symbol} {option.address} 49 |
  • 50 | )} 51 | renderInput={(params) => ( 52 | 58 | {loading ? : null} 59 | 60 | ), 61 | }} 62 | // label={t("label1")} 63 | /> 64 | )} 65 | /> 66 |
    67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /src/view/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import { Erc20 } from "@/config/abi/types"; 2 | import { NATIVE } from "@/config/constants/native"; 3 | import { Token } from "@/config/constants/types"; 4 | import defaultTokenList from "@/config/tokens/index"; 5 | import { useActiveWeb3React } from "@/hooks/useActiveWeb3React"; 6 | import { useERC20 } from "@/hooks/useContract"; 7 | import { isAddress } from "@/utils/isAddress"; 8 | import Button from "@mui/material/Button"; 9 | import TextField from "@mui/material/TextField"; 10 | import { createContext, useEffect, useMemo, useState } from "react"; 11 | import AddressList from "./AddressList"; 12 | import ConfirmPage from "./ConfirmPage"; 13 | import SelectToken from "./SelectToken"; 14 | export const Context = createContext({ confirm, setConfirm: () => {} }); 15 | 16 | export default function Home() { 17 | const { account, chainId, error, activate } = useActiveWeb3React(); 18 | 19 | const [open, setOpen] = useState(false); 20 | const [tokenList, setTokenList] = useState([]); 21 | const [token, setToken] = useState({ address: "", name: "", symbol: "", decimals: 18, chainId }); 22 | 23 | const [searchValue, setSearchValue] = useState(""); 24 | const address = useMemo(() => { 25 | return isAddress(searchValue) ? searchValue : ""; 26 | }, [searchValue]); 27 | 28 | const ERC20Instarnce = useERC20(address); 29 | const [addressValue, setAddressValue] = useState(""); 30 | const [addressList, setAddresslist] = useState([]); 31 | const [sendValue, setSendValue] = useState("1"); 32 | const [confirm, setConfirm] = useState(false); 33 | const [tableData, setTableData] = useState< 34 | { 35 | address: string; 36 | amount: number; 37 | id: number; 38 | }[] 39 | >([]); 40 | 41 | useEffect(() => { 42 | const getErc20Info = async () => { 43 | if (ERC20Instarnce as Erc20) { 44 | try { 45 | const index = tokenList.findIndex((item) => item.address === isAddress(searchValue)); 46 | if (index !== -1) { 47 | return; 48 | } 49 | setOpen(true); 50 | const symbol = await ERC20Instarnce?.symbol(); 51 | const name = await ERC20Instarnce?.name(); 52 | const decimals = await ERC20Instarnce?.decimals(); 53 | const token = { symbol, name, decimals: decimals.toString(), address, chainId }; 54 | setTokenList([...tokenList, token]); 55 | } catch (e) { 56 | // 57 | } 58 | setOpen(false); 59 | } 60 | }; 61 | getErc20Info(); 62 | }, [ERC20Instarnce]); 63 | useEffect(() => { 64 | if (chainId && defaultTokenList[chainId]) { 65 | let _tokenList = [...defaultTokenList[chainId]]; 66 | _tokenList.sort((t1, t2) => { 67 | return t1.symbol.toLowerCase() < t2.symbol.toLowerCase() ? -1 : 1; 68 | }); 69 | _tokenList = [NATIVE[chainId], ..._tokenList]; 70 | setTokenList(_tokenList); 71 | setToken(NATIVE[chainId]); 72 | } else { 73 | setTokenList([]); 74 | setToken({ address: "", name: "", symbol: "", decimals: 18, chainId: chainId }); 75 | } 76 | setConfirm(false); 77 | }, [chainId]); 78 | const onInChange = (value: any) => { 79 | setSearchValue(value); 80 | }; 81 | const onEventChange = (value: any) => { 82 | setToken(value); 83 | }; 84 | const onSetAddressChange = (value: any) => { 85 | setAddressValue(value); 86 | }; 87 | const onSetAddressListChange = (value: any) => { 88 | setAddresslist(value); 89 | }; 90 | 91 | const delAddressList = (index: number) => { 92 | let newArr = [...addressList]; 93 | newArr.splice(index, 1); 94 | setAddresslist(newArr); 95 | 96 | setAddressValue(newArr.join("\n")); 97 | }; 98 | 99 | const errAddressList = useMemo(() => { 100 | const err: { address: string; index: number }[] = []; 101 | addressList.forEach((item, index) => { 102 | if (isAddress(item) !== item && item !== "") { 103 | err.push({ address: item, index }); 104 | } 105 | }); 106 | return err.length ? ( 107 |
    108 | {err.map((item) => { 109 | return ( 110 |
    111 |
    112 | 第{item.index + 1}行 {item.address} 不是一个有效的钱包地址 113 |
    114 |
    115 | ); 116 | })} 117 |
    118 | ) : null; 119 | }, [addressList]); 120 | 121 | return ( 122 |
    123 | {!confirm ? ( 124 |
    125 |
    批量发送代币
    126 | 127 |
    128 | 135 |
    136 |
    137 | 143 |
    144 |
    {errAddressList}
    145 | 146 |
    147 |
    148 |
    每个地址发送:
    149 |
    150 | { 157 | console.log(e.target.value); 158 | 159 | setSendValue(e.target.value); 160 | }} 161 | /> 162 |
    163 |
    164 |
    { 167 | onSetAddressChange( 168 | "0x731c3A53D26487Ea8c9768863CC98BEeaC666666\n0x281Da8e5b33c98BB0600Bbc419250CBF07FD0809", 169 | ); 170 | onSetAddressListChange([ 171 | "0x731c3A53D26487Ea8c9768863CC98BEeaC666666", 172 | "0x281Da8e5b33c98BB0600Bbc419250CBF07FD0809", 173 | ]); 174 | }} 175 | > 176 | 查看例子 177 |
    178 |
    179 |
    180 | ) : ( 181 |
    182 | 183 | 191 | 192 |
    193 | )} 194 | 195 |
    196 | {/* {confirm && ( 197 | 205 | )} */} 206 | {!confirm && ( 207 | 216 | )} 217 |
    218 |
    219 | ); 220 | } 221 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./src/**/*.{html,js,ts,jsx,tsx}"], 3 | 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "suppressImplicitAnyIndexErrors": true, 11 | "strict": false, 12 | // "strictNullChecks": false, 13 | "forceConsistentCasingInFileNames": true, 14 | "module": "ESNext", 15 | "moduleResolution": "Node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | "baseUrl": "./", 21 | "paths": { 22 | "@/*": ["src/*"] 23 | } 24 | }, 25 | "include": ["src"], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import * as path from "path"; 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | resolve: { 8 | alias: { 9 | "@": path.join(__dirname, "src"), 10 | }, 11 | }, 12 | }); 13 | --------------------------------------------------------------------------------