├── .prettierignore ├── public ├── favicon.ico ├── logos │ └── eth.png └── icons │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ └── favicon-128x128.png ├── src ├── assets │ ├── app-icon.png │ ├── app-logo.png │ ├── fa-trash.png │ ├── easy-button.png │ └── fa-speedometer.png ├── boot │ ├── composition-api.ts │ └── error-handler.ts ├── env.d.ts ├── App.vue ├── shims-vue.d.ts ├── utils │ ├── analytics.ts │ ├── ethUsdPrice.ts │ └── alerts.ts ├── pages │ ├── Error404.vue │ ├── Home.vue │ ├── FAQ.vue │ └── Sweep.vue ├── components │ ├── Jazzicon.vue │ ├── SettingsContainer.vue │ ├── models.ts │ ├── SettingsAdvanced.vue │ ├── TransactionPayloadTo.vue │ ├── Avatar.vue │ ├── TransactionPayloadDonation.vue │ └── ConnectWallet.vue ├── router │ ├── routes.ts │ └── index.ts ├── css │ ├── quasar.variables.sass │ └── app.sass ├── index.template.html ├── store │ ├── tx.ts │ └── wallet.ts ├── contracts │ ├── multicall.json │ └── erc20.json └── layouts │ └── BaseLayout.vue ├── .eslintignore ├── babel.config.js ├── .prettierrc ├── .editorconfig ├── tsconfig.json ├── .postcssrc.js ├── .vscode ├── extensions.json └── settings.json ├── quasar.extensions.json ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── .eslintrc.js └── quasar.conf.js /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logos/eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/logos/eth.png -------------------------------------------------------------------------------- /src/assets/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/src/assets/app-icon.png -------------------------------------------------------------------------------- /src/assets/app-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/src/assets/app-logo.png -------------------------------------------------------------------------------- /src/assets/fa-trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/src/assets/fa-trash.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /src-bex/www 3 | /src-capacitor 4 | /src-cordova 5 | /.quasar 6 | /node_modules 7 | -------------------------------------------------------------------------------- /src/assets/easy-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/src/assets/easy-button.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /src/assets/fa-speedometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/src/assets/fa-speedometer.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | presets: ['@quasar/babel-preset-app'], 4 | }; 5 | -------------------------------------------------------------------------------- /public/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mds1/Sweeposaurus/HEAD/public/icons/favicon-128x128.png -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "printWidth": 120, 5 | "singleQuote": true, 6 | "semi": true 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/boot/composition-api.ts: -------------------------------------------------------------------------------- 1 | import VueCompositionApi from '@vue/composition-api'; 2 | import { boot } from 'quasar/wrappers'; 3 | 4 | export default boot(({ Vue }) => { 5 | Vue.use(VueCompositionApi); 6 | }); 7 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | NODE_ENV: string; 4 | VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined; 5 | VUE_ROUTER_BASE: string | undefined; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@quasar/app/tsconfig-preset", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "skipLibCheck": true, 6 | "strict": true 7 | }, 8 | "exclude": ["./node_modules", "./dist"] 9 | } 10 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer'), 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "octref.vetur"], 3 | "unwantedRecommendations": ["hookyqr.beautify", "dbaeumer.jshint", "ms-vscode.vscode-typescript-tslint-plugin"] 4 | } 5 | -------------------------------------------------------------------------------- /quasar.extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "@quasar/dotenv": { 3 | "env_development": ".env", 4 | "env_production": ".env", 5 | "common_root_object": "none", 6 | "create_env_files": false, 7 | "add_env_to_gitignore": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.validation.template": false, 3 | "vetur.format.enable": false, 4 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], 5 | "typescript.tsdk": "node_modules/typescript/lib", 6 | "vetur.experimental.templateInterpolationService": true, 7 | "cSpell.words": ["multicall"] 8 | } 9 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | // Mocks all files ending in `.vue` showing them as plain Vue instances 2 | declare module '*.vue' { 3 | import Vue from 'vue'; 4 | export default Vue; 5 | } 6 | 7 | // For the @metamask/jazzicon package 8 | declare module '@metamask/jazzicon' { 9 | export default function (diameter: number, seed: number): HTMLElement; 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/analytics.ts: -------------------------------------------------------------------------------- 1 | import { Window } from 'components/models'; 2 | 3 | declare let window: Window; 4 | 5 | export default function useAnalytics() { 6 | // Log event `eventName` with goatcounter 7 | function logEvent(eventName: string) { 8 | const t = setInterval(function () { 9 | if (window.goatcounter && window.goatcounter.count) { 10 | clearInterval(t); 11 | window.goatcounter.count({ 12 | path: eventName, 13 | event: true, 14 | }); 15 | } 16 | }, 100); 17 | } 18 | 19 | return { logEvent }; 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/Error404.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /src/components/Jazzicon.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | -------------------------------------------------------------------------------- /src/components/SettingsContainer.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | 27 | 32 | -------------------------------------------------------------------------------- /src/router/routes.ts: -------------------------------------------------------------------------------- 1 | import { RouteConfig } from 'vue-router'; 2 | 3 | const routes: RouteConfig[] = [ 4 | { 5 | path: '/', 6 | component: () => import('layouts/BaseLayout.vue'), 7 | children: [ 8 | { name: 'home', path: '', component: () => import('pages/Home.vue') }, 9 | { name: 'faq', path: '/faq', component: () => import('pages/FAQ.vue') }, 10 | { name: 'sweep', path: '/sweep', component: () => import('pages/Sweep.vue') }, 11 | ], 12 | }, 13 | 14 | // Always leave this as last one, 15 | // but you can also remove it 16 | { 17 | path: '*', 18 | component: () => import('pages/Error404.vue'), 19 | }, 20 | ]; 21 | 22 | export default routes; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env* 2 | *firebase* 3 | .DS_Store 4 | .thumbs.db 5 | node_modules 6 | 7 | # file used to make logo 8 | *.pptx 9 | 10 | # Quasar core related directories 11 | .quasar 12 | /dist 13 | 14 | # Cordova related directories and files 15 | /src-cordova/node_modules 16 | /src-cordova/platforms 17 | /src-cordova/plugins 18 | /src-cordova/www 19 | 20 | # Capacitor related directories and files 21 | /src-capacitor/www 22 | /src-capacitor/node_modules 23 | 24 | # BEX related directories and files 25 | /src-bex/www 26 | /src-bex/js/core 27 | 28 | # Log files 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # Editor directories and files 34 | .idea 35 | *.suo 36 | *.ntvs* 37 | *.njsproj 38 | *.sln 39 | -------------------------------------------------------------------------------- /src/utils/ethUsdPrice.ts: -------------------------------------------------------------------------------- 1 | import { onMounted, ref } from '@vue/composition-api'; 2 | 3 | // Keeping this declaration outside the main function ensures the value is shared between instances, 4 | // allowing us to fetch the price when we first load the app 5 | const ethUsdPrice = ref(0); 6 | 7 | /** 8 | * @notice Gets ETH/USD price 9 | */ 10 | export default function useEthUsdPrice() { 11 | interface Price { 12 | ethereum: { 13 | usd: string; 14 | }; 15 | } 16 | 17 | onMounted(async () => { 18 | try { 19 | const url = 'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd'; 20 | const response = await fetch(url); 21 | const json = (await response.json()) as Price; 22 | ethUsdPrice.value = Number(json.ethereum.usd); 23 | } catch (e) { 24 | console.error(e); 25 | } 26 | }); 27 | 28 | return { ethUsdPrice }; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/models.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { TokenInfo } from '@uniswap/token-lists'; 3 | 4 | export type Signer = ethers.providers.JsonRpcSigner; 5 | export type Provider = ethers.providers.Web3Provider; 6 | export type BigNumber = ethers.BigNumber; 7 | export type BigNumberish = ethers.BigNumberish; 8 | export type BytesLike = ethers.BytesLike; 9 | export type TransactionRequest = ethers.providers.TransactionRequest; 10 | export type TransactionResponse = ethers.providers.TransactionResponse; 11 | 12 | export interface TokenDetails extends TokenInfo { 13 | balance: ethers.BigNumber; 14 | amountToSend?: number | string | undefined; 15 | } 16 | 17 | export interface MulticallResponse { 18 | blockNumber: ethers.BigNumber; 19 | returnData: string[]; 20 | } 21 | 22 | export interface Window { 23 | goatcounter: { 24 | count: (vars: { path: string; event: boolean }) => void; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/SettingsAdvanced.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | -------------------------------------------------------------------------------- /src/components/TransactionPayloadTo.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | -------------------------------------------------------------------------------- /src/css/quasar.variables.sass: -------------------------------------------------------------------------------- 1 | // Quasar Sass (& SCSS) Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Sass/SCSS variables found in Quasar's source Sass/SCSS files. 5 | 6 | // Check documentation for full list of Quasar variables 7 | 8 | // Your own variables (that are declared here) and Quasar's own 9 | // ones will be available out of the box in your .vue/.scss/.sass files 10 | 11 | // It's highly recommended to change the default colors 12 | // to match your app's branding. 13 | // Tip: Use the "Theme Builder" on Quasar's documentation website. 14 | 15 | $primary : #E67635 16 | $secondary : #26A69A 17 | $accent : #9C27B0 18 | 19 | $blue : darken(#55B8E7, 10%) 20 | $orange : darken(#FFAD85, 10%) 21 | 22 | $dark : #1D1D1D 23 | $light : #E2E2E2 24 | 25 | $positive : #21BA45 26 | $negative : #C10015 27 | $info : #31CCEC 28 | $warning : darken(#F2C037,10%) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/boot/error-handler.ts: -------------------------------------------------------------------------------- 1 | import { boot } from 'quasar/wrappers'; 2 | import useAlerts from 'src/utils/alerts'; 3 | const { handleError } = useAlerts(); 4 | 5 | export default boot(({ Vue }) => { 6 | // Source: https://stackoverflow.com/questions/52071212/how-to-implement-global-error-handling-in-vue 7 | 8 | // `info` is a Vue-specific error info, e.g. which lifecycle hook 9 | // the error was found in. Only available in 2.2.0+ 10 | Vue.config.errorHandler = function (err, _vm, info) { 11 | console.log('Error info:', info); 12 | handleError(err); 13 | }; 14 | 15 | // General JS (non-Vue) error handler 16 | window.onerror = function (msg, url, line, col, err) { 17 | if (err) { 18 | console.log('Error params:', msg, url, line, col); 19 | handleError(err); 20 | } 21 | }; 22 | 23 | // Handle promise rejections 24 | window.addEventListener('unhandledrejection', function (event) { 25 | // event.promise contains the promise object 26 | // event.reason contains the reason for the rejection 27 | throw new Error(event.reason); // this converts promise rejection to ordinary JS error 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { route } from 'quasar/wrappers'; 2 | import VueRouter from 'vue-router'; 3 | import routes from './routes'; 4 | 5 | /* 6 | * If not building with SSR mode, you can 7 | * directly export the Router instantiation 8 | */ 9 | 10 | export default route(function ({ Vue }) { 11 | Vue.use(VueRouter); 12 | 13 | const Router = new VueRouter({ 14 | scrollBehavior: () => ({ x: 0, y: 0 }), 15 | routes, 16 | 17 | // Leave these as is and change from quasar.conf.js instead! 18 | // quasar.conf.js -> build -> vueRouterMode 19 | // quasar.conf.js -> build -> publicPath 20 | mode: process.env.VUE_ROUTER_MODE, 21 | base: process.env.VUE_ROUTER_BASE, 22 | }); 23 | 24 | Router.beforeEach((to, from, next) => { 25 | // Only allow user to navigate directly to home page 26 | // Navigating to home page is allowed. Navigating to other pages without visiting 27 | // home page is not allowed. This is to ensure the user connects a wallet first 28 | if (to.name === 'home' || to.name === 'faq') { 29 | next(); 30 | } else if (from.name === null) { 31 | next({ name: 'home' }); 32 | } 33 | next(); 34 | }); 35 | 36 | return Router; 37 | }); 38 | -------------------------------------------------------------------------------- /src/utils/alerts.ts: -------------------------------------------------------------------------------- 1 | import { Notify } from 'quasar'; 2 | 3 | export default function useAlerts() { 4 | /** 5 | * Present notification alert to the user 6 | * @param {string} color alert color, choose positive, negative, warning, info, or others 7 | * @param {string} message message to display on notification 8 | */ 9 | function notifyUser(color: string, message: string) { 10 | Notify.create({ 11 | color, 12 | message, 13 | timeout: color.toLowerCase() === 'negative' ? 10000 : 5000, 14 | position: 'top', 15 | actions: [{ label: 'Dismiss', color: 'white' }], 16 | }); 17 | } 18 | 19 | /** 20 | * Show error message to user 21 | * @param {Any} err Error object thrown 22 | * @param {Any} msg Optional, fallback error message if one is not provided by the err object 23 | */ 24 | function handleError(err: Error, msg = 'An unknown error occurred') { 25 | console.error(err); 26 | if (!err) notifyUser('negative', msg); 27 | else if ('message' in err) notifyUser('negative', err.message); 28 | else if (typeof err === 'string') notifyUser('negative', err); 29 | else notifyUser('negative', msg); 30 | } 31 | 32 | return { 33 | notifyUser, 34 | handleError, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/components/Avatar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 43 | 44 | 48 | -------------------------------------------------------------------------------- /src/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 42 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= productName %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sweeposaurus 2 | 3 |
4 |

Say hello to Sweeposaurus. He'll make it easy to sweep all your tokens to a new address!

5 | 6 |

Start sweeping at sweeposaurus.com 7 |

8 | 9 | ## About 10 | 11 | Sweeposaurus is simple. It works as follows: 12 | 13 | 1. Connect a wallet 14 | 1. Scan for all tokens held by your wallet. Right now this is done using CoinGecko's [token list](https://tokenlists.org/token-list?url=https://tokens.coingecko.com/uniswap/all.json), which means it scans for over 1600 tokens! 15 | 1. Specify an address to send tokens to, along with the gas price for each transaction. 16 | 1. A table is populated showing your balance of each token. An amount column is used change how many of that token to send. Leave the value alone to send the max amount, set it to zero to send nothing, or set it to any other value you want. 17 | 1. Click "Send", and you'll get one transaction prompt for each token which will send them to your new address 18 | 19 | ## Development 20 | 21 | If you need to generate new icons, use the command below: 22 | 23 | ```bash 24 | icongenie generate --mode spa,pwa --icon src/assets/app-logo.png 25 | ``` 26 | 27 | Otherwise, you can get setup for development as follows: 28 | 29 | ```bash 30 | # Install the dependencies 31 | yarn 32 | 33 | # Lint files 34 | yarn run lint 35 | 36 | # Run prettier 37 | yarn prettier 38 | 39 | # Start the app in web development mode 40 | yarn dev 41 | 42 | # Build app for production 43 | yarn build 44 | 45 | # Deploy app 46 | yarn deploy 47 | ``` 48 | 49 | ## Contributing 50 | 51 | Pull requests are welcome! For major changes, please open an issue first to discuss the change. 52 | -------------------------------------------------------------------------------- /src/css/app.sass: -------------------------------------------------------------------------------- 1 | // app global css in Sass form 2 | * 3 | font-family: Avenir,Helvetica,Arial,sans-serif 4 | 5 | h1 6 | font-size: 5em 7 | 8 | .card-border-no-hover 9 | border: 1px solid rgba($light, 0.1) 10 | 11 | .card-border 12 | border: 1px solid rgba($light, 0.1) 13 | 14 | .card-border:hover 15 | border: 1px solid $primary 16 | cursor: pointer 17 | 18 | .text-gradient 19 | background-color: $primary 20 | background-clip: text 21 | background-image: linear-gradient(130deg, $orange 20%, $blue 100%) 22 | color: rgba(0,0,0,0) 23 | -webkit-background-clip: text 24 | -moz-background-clip: text 25 | -webkit-text-fill-color: transparent 26 | -moz-text-fill-color: transparent 27 | 28 | .body--light 29 | .dark-toggle 30 | color: $dark 31 | 32 | .hyperlink 33 | color: $primary 34 | text-decoration: underline 35 | cursor: pointer 36 | // Without this, the underline extends into the space for some reason 37 | // solution source: https://stackoverflow.com/questions/23182904/css-underline-text-but-ignore-the-spaces 38 | display: inline-block 39 | 40 | .body--dark 41 | .dark-toggle 42 | color: $light 43 | 44 | .hyperlink 45 | color: $primary 46 | text-decoration: underline 47 | cursor: pointer 48 | // Without this, the underline extends into the space for some reason 49 | // solution source: https://stackoverflow.com/questions/23182904/css-underline-text-but-ignore-the-spaces 50 | display: inline-block 51 | 52 | .is-beta 53 | border: 1px solid $warning 54 | border-radius: 10px 55 | margin: 0 auto 1rem 56 | max-width: 750px 57 | padding: 0.75rem 58 | 59 | .container 60 | margin: 0 auto 61 | max-width: 500px 62 | 63 | .top-border 64 | border-top: 0.2rem solid $primary 65 | 66 | .no-text-decoration 67 | text-decoration: none 68 | -------------------------------------------------------------------------------- /src/store/tx.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref } from '@vue/composition-api'; 2 | import { ethers } from 'ethers'; 3 | import { BigNumberish, BytesLike } from 'components/models'; 4 | 5 | /** 6 | * This store holds the transaction payload that will be used 7 | */ 8 | 9 | // State =========================================================================================== 10 | const to = ref(''); 11 | const nonce = ref(); 12 | const gasLimit = ref(ethers.BigNumber.from('21000')); 13 | const data = ref(''); 14 | const value = ref('0'); 15 | 16 | export default function useTxStore() { 17 | // Mutations ===================================================================================== 18 | function setTxTo(val: string) { 19 | to.value = ethers.utils.getAddress(val); 20 | } 21 | function setTxNonce(val: number) { 22 | // Not currently used 23 | nonce.value = val; 24 | } 25 | function setTxGasLimit(val: BigNumberish) { 26 | // Not currently used 27 | gasLimit.value = val; 28 | } 29 | function setTxData(val: BytesLike) { 30 | // Not currently used 31 | data.value = val; 32 | } 33 | function setTxValue(val: BigNumberish) { 34 | // Used to indicate donation amount 35 | value.value = val; 36 | } 37 | 38 | // Getters ======================================================================================= 39 | // Transaction payload to send 40 | const txPayload = computed(() => { 41 | return { 42 | to: to.value, 43 | nonce: nonce.value, 44 | gasLimit: gasLimit.value, 45 | data: data.value, 46 | value: value.value, 47 | }; 48 | }); 49 | 50 | return { 51 | setTxTo, 52 | setTxNonce, 53 | setTxGasLimit, 54 | setTxData, 55 | setTxValue, 56 | txPayload, 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /src/contracts/multicall.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": "0xcA11bde05977b3631167028862bE2a173976CA11", 3 | "abi": [ 4 | "function aggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes[] returnData)", 5 | "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)", 6 | "function aggregate3Value(tuple(address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)", 7 | "function blockAndAggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)", 8 | "function getBasefee() view returns (uint256 basefee)", 9 | "function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)", 10 | "function getBlockNumber() view returns (uint256 blockNumber)", 11 | "function getChainId() view returns (uint256 chainid)", 12 | "function getCurrentBlockCoinbase() view returns (address coinbase)", 13 | "function getCurrentBlockDifficulty() view returns (uint256 difficulty)", 14 | "function getCurrentBlockGasLimit() view returns (uint256 gaslimit)", 15 | "function getCurrentBlockTimestamp() view returns (uint256 timestamp)", 16 | "function getEthBalance(address addr) view returns (uint256 balance)", 17 | "function getLastBlockHash() view returns (bytes32 blockHash)", 18 | "function tryAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)", 19 | "function tryBlockAndAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sweeposaurus", 3 | "version": "0.0.1", 4 | "description": "Sweep all of your tokens to another address", 5 | "productName": "Sweeposaurus", 6 | "author": "Matt Solomon ", 7 | "license": "MIT", 8 | "private": true, 9 | "scripts": { 10 | "lint": "eslint --ext .js,.ts,.vue ./", 11 | "test": "echo \"No test specified\" && exit 0", 12 | "prettier": "prettier --write .", 13 | "dev": "quasar dev", 14 | "build": "quasar build", 15 | "deploy": "yarn build && firebase deploy" 16 | }, 17 | "husky": { 18 | "hooks": { 19 | "pre-commit": "lint-staged && quasar build", 20 | "pre-push": "lint-staged && quasar build" 21 | } 22 | }, 23 | "dependencies": { 24 | "@metamask/jazzicon": "^2.0.0", 25 | "@quasar/extras": "^1.0.0", 26 | "@unilogin/provider": "^0.6.1", 27 | "@uniswap/token-lists": "^1.0.0-beta.19", 28 | "@vue/composition-api": "^1.0.0-beta.22", 29 | "bnc-onboard": "^1.39.0", 30 | "core-js": "^3.6.5", 31 | "ethers": "^5.6.9", 32 | "quasar": "^1.0.0" 33 | }, 34 | "devDependencies": { 35 | "@quasar/app": "^2.0.0", 36 | "@quasar/quasar-app-extension-dotenv": "^1.0.5", 37 | "@types/node": "^14.14.20", 38 | "@typescript-eslint/eslint-plugin": "^4.12.0", 39 | "@typescript-eslint/parser": "^4.12.0", 40 | "babel-eslint": "^10.0.1", 41 | "eslint": "^7.17.0", 42 | "eslint-config-prettier": "^7.1.0", 43 | "eslint-loader": "^4.0.2", 44 | "eslint-plugin-vue": "^7.4.1", 45 | "husky": "^4.2.5", 46 | "lint-staged": "^10.2.11", 47 | "prettier": "^2.0.5" 48 | }, 49 | "browserslist": [ 50 | "last 10 Chrome versions", 51 | "last 10 Firefox versions", 52 | "last 4 Edge versions", 53 | "last 7 Safari versions", 54 | "last 8 Android versions", 55 | "last 8 ChromeAndroid versions", 56 | "last 8 FirefoxAndroid versions", 57 | "last 10 iOS versions", 58 | "last 5 Opera versions" 59 | ], 60 | "engines": { 61 | "node": ">= 10.18.1", 62 | "npm": ">= 6.13.4", 63 | "yarn": ">= 1.21.1" 64 | }, 65 | "lint-staged": { 66 | "*.{js,ts,vue}": [ 67 | "prettier --write", 68 | "eslint --cache --fix" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/components/TransactionPayloadDonation.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 71 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | module.exports = { 3 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy 4 | // This option interrupts the configuration hierarchy at this file 5 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) 6 | root: true, 7 | 8 | // https://eslint.vuejs.org/user-guide/#how-to-use-custom-parser 9 | // Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working 10 | // `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted 11 | parserOptions: { 12 | // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#configuration 13 | // https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#eslint 14 | // Needed to make the parser take into account 'vue' files 15 | extraFileExtensions: ['.vue'], 16 | parser: '@typescript-eslint/parser', 17 | project: resolve(__dirname, './tsconfig.json'), 18 | tsconfigRootDir: __dirname, 19 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 20 | sourceType: 'module', // Allows for the use of imports 21 | }, 22 | 23 | env: { 24 | browser: true, 25 | }, 26 | 27 | // Rules order is important, please avoid shuffling them 28 | extends: [ 29 | // Base ESLint recommended rules 30 | // 'eslint:recommended', 31 | 32 | // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage 33 | // ESLint typescript rules 34 | 'plugin:@typescript-eslint/recommended', 35 | // consider disabling this class of rules if linting takes too long 36 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 37 | 38 | // Uncomment any of the lines below to choose desired strictness, 39 | // but leave only one uncommented! 40 | // See https://eslint.vuejs.org/rules/#available-rules 41 | // 'plugin:vue/essential', // Priority A: Essential (Error Prevention) 42 | // 'plugin:vue/strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 43 | 'plugin:vue/recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) 44 | 45 | // https://github.com/prettier/eslint-config-prettier#installation 46 | // usage with Prettier, provided by 'eslint-config-prettier'. 47 | 'prettier', 48 | 'prettier/@typescript-eslint', 49 | 'prettier/vue', 50 | ], 51 | 52 | plugins: [ 53 | // required to apply rules which need type information 54 | '@typescript-eslint', 55 | 56 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-file 57 | // required to lint *.vue files 58 | 'vue', 59 | 60 | // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 61 | // Prettier has not been included as plugin to avoid performance impact 62 | // add it as an extension for your IDE 63 | ], 64 | 65 | globals: { 66 | ga: true, // Google Analytics 67 | cordova: true, 68 | __statics: true, 69 | process: true, 70 | Capacitor: true, 71 | chrome: true, 72 | }, 73 | 74 | // add your custom rules here 75 | rules: { 76 | 'prefer-promise-reject-errors': 'off', 77 | 78 | // TypeScript 79 | quotes: ['warn', 'single', { avoidEscape: true }], 80 | '@typescript-eslint/explicit-function-return-type': 'off', 81 | '@typescript-eslint/explicit-module-boundary-types': 'off', 82 | 83 | // allow debugger during development only 84 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /src/components/ConnectWallet.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 105 | -------------------------------------------------------------------------------- /src/contracts/erc20.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [ 3 | { 4 | "constant": true, 5 | "inputs": [], 6 | "name": "name", 7 | "outputs": [{ "name": "", "type": "string" }], 8 | "payable": false, 9 | "stateMutability": "view", 10 | "type": "function" 11 | }, 12 | { 13 | "constant": false, 14 | "inputs": [ 15 | { "name": "_spender", "type": "address" }, 16 | { "name": "_value", "type": "uint256" } 17 | ], 18 | "name": "approve", 19 | "outputs": [{ "name": "", "type": "bool" }], 20 | "payable": false, 21 | "stateMutability": "nonpayable", 22 | "type": "function" 23 | }, 24 | { 25 | "constant": true, 26 | "inputs": [], 27 | "name": "totalSupply", 28 | "outputs": [{ "name": "", "type": "uint256" }], 29 | "payable": false, 30 | "stateMutability": "view", 31 | "type": "function" 32 | }, 33 | { 34 | "constant": false, 35 | "inputs": [ 36 | { "name": "_from", "type": "address" }, 37 | { "name": "_to", "type": "address" }, 38 | { "name": "_value", "type": "uint256" } 39 | ], 40 | "name": "transferFrom", 41 | "outputs": [{ "name": "", "type": "bool" }], 42 | "payable": false, 43 | "stateMutability": "nonpayable", 44 | "type": "function" 45 | }, 46 | { 47 | "constant": true, 48 | "inputs": [], 49 | "name": "decimals", 50 | "outputs": [{ "name": "", "type": "uint8" }], 51 | "payable": false, 52 | "stateMutability": "view", 53 | "type": "function" 54 | }, 55 | { 56 | "constant": true, 57 | "inputs": [{ "name": "_owner", "type": "address" }], 58 | "name": "balanceOf", 59 | "outputs": [{ "name": "balance", "type": "uint256" }], 60 | "payable": false, 61 | "stateMutability": "view", 62 | "type": "function" 63 | }, 64 | { 65 | "constant": true, 66 | "inputs": [], 67 | "name": "symbol", 68 | "outputs": [{ "name": "", "type": "string" }], 69 | "payable": false, 70 | "stateMutability": "view", 71 | "type": "function" 72 | }, 73 | { 74 | "constant": false, 75 | "inputs": [ 76 | { "name": "_to", "type": "address" }, 77 | { "name": "_value", "type": "uint256" } 78 | ], 79 | "name": "transfer", 80 | "outputs": [{ "name": "", "type": "bool" }], 81 | "payable": false, 82 | "stateMutability": "nonpayable", 83 | "type": "function" 84 | }, 85 | { 86 | "constant": true, 87 | "inputs": [ 88 | { "name": "_owner", "type": "address" }, 89 | { "name": "_spender", "type": "address" } 90 | ], 91 | "name": "allowance", 92 | "outputs": [{ "name": "", "type": "uint256" }], 93 | "payable": false, 94 | "stateMutability": "view", 95 | "type": "function" 96 | }, 97 | { "payable": true, "stateMutability": "payable", "type": "fallback" }, 98 | { 99 | "anonymous": false, 100 | "inputs": [ 101 | { "indexed": true, "name": "owner", "type": "address" }, 102 | { "indexed": true, "name": "spender", "type": "address" }, 103 | { "indexed": false, "name": "value", "type": "uint256" } 104 | ], 105 | "name": "Approval", 106 | "type": "event" 107 | }, 108 | { 109 | "anonymous": false, 110 | "inputs": [ 111 | { "indexed": true, "name": "from", "type": "address" }, 112 | { "indexed": true, "name": "to", "type": "address" }, 113 | { "indexed": false, "name": "value", "type": "uint256" } 114 | ], 115 | "name": "Transfer", 116 | "type": "event" 117 | } 118 | ] 119 | } 120 | -------------------------------------------------------------------------------- /src/layouts/BaseLayout.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 117 | 118 | 122 | -------------------------------------------------------------------------------- /src/pages/FAQ.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | 125 | -------------------------------------------------------------------------------- /src/store/wallet.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref } from '@vue/composition-api'; 2 | import { ethers } from 'ethers'; 3 | import { StaticJsonRpcProvider } from '@ethersproject/providers'; 4 | import { TokenList } from '@uniswap/token-lists'; 5 | import { MulticallResponse, Signer, Provider, TokenDetails } from 'components/models'; 6 | import multicallInfo from 'src/contracts/multicall.json'; 7 | import erc20 from 'src/contracts/erc20.json'; 8 | import useAnalytics from 'src/utils/analytics'; 9 | 10 | const { Zero } = ethers.constants; 11 | 12 | export const MAINNET_RPC_URL = `https://mainnet.infura.io/v3/${String(process.env.INFURA_ID)}`; 13 | export const MAINNET_PROVIDER = new StaticJsonRpcProvider(MAINNET_RPC_URL); 14 | 15 | // Returns an address with the following format: 0x1234...abcd 16 | const formatAddress = (address: string) => `${address.slice(0, 6)}...${address.slice(38)}`; 17 | 18 | /** 19 | * State is handled in reusable components, where each component is its own self-contained 20 | * file consisting of one function defined used the composition API. 21 | * 22 | * Since we want the wallet state to be shared between all instances when this file is imported, 23 | * we defined state outside of the function definition. 24 | */ 25 | 26 | // ============================================= State ============================================= 27 | // We do not publicly expose the state to provide control over when and how it's changed. It 28 | // can only be changed through actions and mutations, and it can only be accessed with getters. 29 | // As a result, only actions, mutations, and getters are returned from this function. 30 | const provider = ref(undefined); 31 | const signer = ref(undefined); 32 | const chainId = ref(undefined); 33 | const userAddress = ref(undefined); 34 | const userDisplayName = ref(undefined); 35 | const avatar = ref(''); 36 | const balances = ref([]); 37 | 38 | export default function useWalletStore() { 39 | // =========================================== Actions =========================================== 40 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 41 | async function setProvider(p: any) { 42 | provider.value = new ethers.providers.Web3Provider(p); 43 | signer.value = provider.value.getSigner(); 44 | const [_userAddress, _network] = await Promise.all([signer.value.getAddress(), provider.value.getNetwork()]); 45 | userAddress.value = _userAddress; 46 | chainId.value = _network.chainId; 47 | userDisplayName.value = formatAddress(_userAddress); 48 | 49 | const userEns = await MAINNET_PROVIDER.lookupAddress(_userAddress); 50 | if (typeof userEns === 'string') { 51 | // ENS address must exist 52 | avatar.value = await MAINNET_PROVIDER.getAvatar(userEns); 53 | } 54 | userDisplayName.value = userEns || formatAddress(_userAddress); 55 | } 56 | 57 | async function fetchTokenList() { 58 | const jsonFetch = (url: string) => fetch(url).then((res) => res.json()); 59 | const url = 'https://tokens.uniswap.org/'; // Includes tokens for various networks 60 | const response = (await jsonFetch(url)) as TokenList; 61 | return response.tokens.filter((token) => token.chainId === chainId.value); 62 | } 63 | 64 | async function scan() { 65 | const { logEvent } = useAnalytics(); 66 | logEvent('scan-started'); 67 | 68 | // Get token list 69 | const tokenList = await fetchTokenList(); 70 | 71 | // Get Multicall instance 72 | const multicall = new ethers.Contract(multicallInfo.address, multicallInfo.abi, signer.value); 73 | 74 | // Generate balance calls using Multicall contract 75 | const userAddr = userAddress.value || (await signer.value?.getAddress()); 76 | const calls = tokenList.map((token) => { 77 | const { address: tokenAddress } = token; 78 | const tokenContract = new ethers.Contract(tokenAddress, erc20.abi, signer.value); 79 | return { 80 | target: tokenAddress, 81 | callData: tokenContract.interface.encodeFunctionData('balanceOf', [userAddr]), 82 | }; 83 | }); 84 | 85 | // Generate array of promises to get token balances + ETH balance 86 | const ethBalancePromise = signer.value?.getBalance(); 87 | const promises = [multicall.callStatic.aggregate(calls), ethBalancePromise]; 88 | 89 | // Wait for promises to resolve 90 | const responses = await Promise.all(promises); 91 | const multicallResponse = responses[0] as MulticallResponse; 92 | const ethResponse = responses[1] as ethers.BigNumber; 93 | const tokenBalances = multicallResponse.returnData; // token balances from multicall 94 | 95 | // Create array of all tokens with their balance and only keep nonzero ones 96 | balances.value = tokenList 97 | .map((token, index) => { 98 | // `tokenBalances[index] === '0x'` occurs when a token in the token list is an EOA or non ERC-20 contract, so 99 | // instead of returning a balance we get no data 100 | const balance = tokenBalances[index] === '0x' ? Zero : ethers.BigNumber.from(tokenBalances[index]); 101 | return { ...token, balance, amountToSend: 'max' }; 102 | }) 103 | .filter((token) => token.balance.gt(Zero)) 104 | .sort((token1, token2) => token1.symbol.localeCompare(token2.symbol)); 105 | 106 | // Append ETH to the list 107 | const ethToken = { 108 | chainId: 1, 109 | address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', 110 | name: 'Ether', 111 | decimals: 18, 112 | symbol: 'ETH', 113 | logoURI: 'logos/eth.png', 114 | }; 115 | balances.value.push({ 116 | ...ethToken, 117 | balance: ethResponse, 118 | amountToSend: 'max', 119 | }); 120 | } 121 | 122 | return { 123 | provider: computed(() => provider.value), 124 | signer: computed(() => signer.value), 125 | avatar: computed(() => avatar.value), 126 | chainId: computed(() => chainId.value), 127 | userAddress: computed(() => userAddress.value), 128 | userDisplayName: computed(() => userDisplayName.value), 129 | setProvider, 130 | scan, 131 | balances: computed(() => balances.value), 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file runs in a Node context (it's NOT transpiled by Babel), so use only 3 | * the ES6 features that are supported by your Node version. https://node.green/ 4 | */ 5 | 6 | // Configuration for your app 7 | // https://quasar.dev/quasar-cli/quasar-conf-js 8 | /* eslint-env node */ 9 | /* eslint-disable @typescript-eslint/no-var-requires */ 10 | const { configure } = require('quasar/wrappers'); 11 | 12 | module.exports = configure(function (ctx) { 13 | return { 14 | // https://quasar.dev/quasar-cli/supporting-ts 15 | supportTS: { 16 | tsCheckerConfig: { 17 | eslint: true, 18 | }, 19 | }, 20 | 21 | // https://quasar.dev/quasar-cli/prefetch-feature 22 | // preFetch: true, 23 | 24 | // app boot file (/src/boot) 25 | // --> boot files are part of "main.js" 26 | // https://quasar.dev/quasar-cli/boot-files 27 | boot: ['composition-api', 'error-handler'], 28 | 29 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css 30 | css: ['app.sass'], 31 | 32 | // https://github.com/quasarframework/quasar/tree/dev/extras 33 | extras: [ 34 | // 'ionicons-v4', 35 | // 'mdi-v5', 36 | 'fontawesome-v5', 37 | // 'eva-icons', 38 | // 'themify', 39 | // 'line-awesome', 40 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 41 | 42 | // 'roboto-font', // optional, you are not bound to it 43 | // 'material-icons', // optional, you are not bound to it 44 | ], 45 | 46 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build 47 | build: { 48 | vueRouterMode: 'history', // available values: 'hash', 'history' 49 | 50 | // transpile: false, 51 | 52 | // Add dependencies for transpiling with Babel (Array of string/regex) 53 | // (from node_modules, which are by default not transpiled). 54 | // Applies only if "transpile" is set to true. 55 | // transpileDependencies: [], 56 | 57 | // rtl: false, // https://quasar.dev/options/rtl-support 58 | // preloadChunks: true, 59 | // showProgress: false, 60 | // gzip: true, 61 | // analyze: true, 62 | 63 | // Options below are automatically set depending on the env, set them if you want to override 64 | // extractCSS: false, 65 | 66 | // https://quasar.dev/quasar-cli/handling-webpack 67 | extendWebpack(cfg) { 68 | // linting is slow in TS projects, we execute it only for production builds 69 | if (ctx.prod) { 70 | cfg.module.rules.push({ 71 | enforce: 'pre', 72 | test: /\.(js|vue)$/, 73 | loader: 'eslint-loader', 74 | exclude: /node_modules/, 75 | }); 76 | } 77 | }, 78 | }, 79 | 80 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer 81 | devServer: { 82 | https: false, 83 | port: 8080, 84 | open: true, // opens browser window automatically 85 | }, 86 | 87 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework 88 | framework: { 89 | iconSet: 'fontawesome-v5', // Quasar icon set 90 | lang: 'en-us', // Quasar language pack 91 | config: {}, 92 | 93 | // Possible values for "importStrategy": 94 | // * 'auto' - (DEFAULT) Auto-import needed Quasar components & directives 95 | // * 'all' - Manually specify what to import 96 | importStrategy: 'auto', 97 | 98 | // Quasar plugins 99 | plugins: ['LocalStorage', 'Notify'], 100 | }, 101 | 102 | // animations: 'all', // --- includes all animations 103 | // https://quasar.dev/options/animations 104 | animations: [], 105 | 106 | // https://quasar.dev/quasar-cli/developing-ssr/configuring-ssr 107 | ssr: { 108 | pwa: false, 109 | }, 110 | 111 | // https://quasar.dev/quasar-cli/developing-pwa/configuring-pwa 112 | pwa: { 113 | workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' 114 | workboxOptions: {}, // only for GenerateSW 115 | manifest: { 116 | name: 'Sweeposaurus', 117 | short_name: 'Sweeposaurus', 118 | description: 'Sweep all of your tokens to another address', 119 | display: 'standalone', 120 | orientation: 'portrait', 121 | background_color: '#ffffff', 122 | theme_color: '#027be3', 123 | icons: [ 124 | { 125 | src: 'icons/icon-128x128.png', 126 | sizes: '128x128', 127 | type: 'image/png', 128 | }, 129 | { 130 | src: 'icons/icon-192x192.png', 131 | sizes: '192x192', 132 | type: 'image/png', 133 | }, 134 | { 135 | src: 'icons/icon-256x256.png', 136 | sizes: '256x256', 137 | type: 'image/png', 138 | }, 139 | { 140 | src: 'icons/icon-384x384.png', 141 | sizes: '384x384', 142 | type: 'image/png', 143 | }, 144 | { 145 | src: 'icons/icon-512x512.png', 146 | sizes: '512x512', 147 | type: 'image/png', 148 | }, 149 | ], 150 | }, 151 | }, 152 | 153 | // Full list of options: https://quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova 154 | cordova: { 155 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing 156 | }, 157 | 158 | // Full list of options: https://quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor 159 | capacitor: { 160 | hideSplashscreen: true, 161 | }, 162 | 163 | // Full list of options: https://quasar.dev/quasar-cli/developing-electron-apps/configuring-electron 164 | electron: { 165 | bundler: 'packager', // 'packager' or 'builder' 166 | 167 | packager: { 168 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 169 | // OS X / Mac App Store 170 | // appBundleId: '', 171 | // appCategoryType: '', 172 | // osxSign: '', 173 | // protocol: 'myapp://path', 174 | // Windows only 175 | // win32metadata: { ... } 176 | }, 177 | 178 | builder: { 179 | // https://www.electron.build/configuration/configuration 180 | 181 | appId: 'sweeposaurus', 182 | }, 183 | 184 | // More info: https://quasar.dev/quasar-cli/developing-electron-apps/node-integration 185 | nodeIntegration: true, 186 | 187 | extendWebpack(/* cfg */) { 188 | // do something with Electron main process Webpack cfg 189 | // chainWebpack also available besides this extendWebpack 190 | }, 191 | }, 192 | }; 193 | }); 194 | -------------------------------------------------------------------------------- /src/pages/Sweep.vue: -------------------------------------------------------------------------------- 1 | 106 | 107 | 264 | --------------------------------------------------------------------------------