├── .env-template
├── .gitignore
├── assets
├── chain-alert.png
└── web3modal.png
├── frontend
├── babel.config.js
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── bootstrap
│ │ ├── dashboard.css
│ │ └── bootstrap.min.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── contracts
│ │ ├── addresses.json
│ │ └── Calc.json
│ ├── store
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── contracts.js
│ │ │ └── accounts.js
│ ├── main.js
│ ├── App.vue
│ ├── pages
│ │ ├── Home.vue
│ │ ├── Profile.vue
│ │ └── SetValue.vue
│ ├── router.js
│ └── components
│ │ ├── Navbar.vue
│ │ └── Sidebar.vue
└── package.json
├── contracts
└── Calc.sol
├── package.json
├── test
└── Calc.test.js
├── hardhat.config.js
├── README.md
└── scripts
└── deploy.js
/.env-template:
--------------------------------------------------------------------------------
1 | ALCHEMY_API_KEY=""
2 | ALCHEMY_DEPLOYMENT_KEY=""
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 |
4 | #Hardhat files
5 | /cache
6 | /artifacts
7 |
--------------------------------------------------------------------------------
/assets/chain-alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remote-gildor/hardhat-vue-starter/HEAD/assets/chain-alert.png
--------------------------------------------------------------------------------
/assets/web3modal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remote-gildor/hardhat-vue-starter/HEAD/assets/web3modal.png
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remote-gildor/hardhat-vue-starter/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remote-gildor/hardhat-vue-starter/HEAD/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/frontend/src/contracts/addresses.json:
--------------------------------------------------------------------------------
1 | {
2 | "Calc": {
3 | "1337":"0x021dCBE2778A7911E2075D74bd8f6a527A1846d3",
4 | "31337":"0x5FbDB2315678afecb367f032d93F642f64180aa3"
5 | }
6 | }
--------------------------------------------------------------------------------
/frontend/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import accounts from "./modules/accounts";
4 | import contracts from "./modules/contracts";
5 |
6 | Vue.use(Vuex);
7 |
8 | export default new Vuex.Store({
9 | modules: {
10 | accounts,
11 | contracts
12 | }
13 | });
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import store from "./store/index.js";
4 | import router from "./router.js";
5 | import Toasted from 'vue-toasted';
6 |
7 | Vue.use(Toasted);
8 | Vue.config.productionTip = false;
9 |
10 | new Vue({
11 | router,
12 | store,
13 | render: h => h(App),
14 | }).$mount('#app');
15 |
--------------------------------------------------------------------------------
/contracts/Calc.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity =0.7.3;
3 |
4 | contract Calc {
5 | uint private num = 0;
6 |
7 | event NumberSet(address _from, uint value);
8 |
9 | function getNum() public view returns(uint) {
10 | return num;
11 | }
12 |
13 | function setNum(uint _num) public {
14 | num = _num;
15 |
16 | emit NumberSet(msg.sender, _num);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hardhat-vue-starter",
3 | "version": "1.0.0",
4 | "description": "An example hardhat dapp to jumpstart your project.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "Gildor",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "@nomiclabs/hardhat-ethers": "^2.0.1",
14 | "@nomiclabs/hardhat-waffle": "^2.0.1",
15 | "chai": "^4.2.0",
16 | "ethereum-waffle": "^3.2.1",
17 | "ethers": "^5.0.24",
18 | "hardhat": "^2.0.6",
19 | "hardhat-deploy": "^0.7.0-beta.39"
20 | },
21 | "dependencies": {
22 | "dotenv": "^8.2.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/test/Calc.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require("chai");
2 |
3 | describe("Calc contract", function() {
4 | let calcInstance = null;
5 |
6 | beforeEach(async () => {
7 | const Calc = await ethers.getContractFactory("Calc");
8 | calcInstance = await Calc.deploy();
9 | });
10 |
11 |
12 | it("gets the num value before a set function is called", async function() {
13 | const numValue = await calcInstance.getNum();
14 | expect(numValue).to.equal(0);
15 | });
16 |
17 | it("set the new num value and check if it changed", async function() {
18 | await calcInstance.setNum(5);
19 |
20 | const numValue = await calcInstance.getNum();
21 | expect(numValue).to.equal(5);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/hardhat.config.js:
--------------------------------------------------------------------------------
1 | require("@nomiclabs/hardhat-waffle");
2 | require('dotenv').config();
3 |
4 | /**
5 | * @type import('hardhat/config').HardhatUserConfig
6 | */
7 | module.exports = {
8 | defaultNetwork: "hardhat",
9 |
10 | networks: {
11 | hardhat: {},
12 | ganache: {
13 | url: "http://127.0.0.1:7545/",
14 | saveDeployments: true
15 | }
16 | //goerli: {
17 | // url: "https://eth-goerli.alchemyapi.io/v2/" + process.env.ALCHEMY_API_KEY,
18 | // accounts: [process.env.ALCHEMY_DEPLOYMENT_KEY]
19 | //}
20 | },
21 |
22 | paths: {
23 | sources: "./contracts",
24 | tests: "./test",
25 | cache: "./cache",
26 | artifacts: "./artifacts"
27 | },
28 |
29 | solidity: {
30 | version: "0.7.3",
31 | settings: {
32 | optimizer: {
33 | enabled: true,
34 | runs: 200
35 | }
36 | }
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/frontend/src/pages/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Welcome to Value Setter!
5 |
6 |
7 |
This is a starter template with a dummy smart contract (Calc.sol) to jumpstart your project.
8 |
9 |
10 | Your current chain: {{getChainName}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hardhat Vue Starter by Gildor
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/frontend/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Router from "vue-router";
3 | import Home from "./pages/Home";
4 | import Profile from "./pages/Profile";
5 | import SetValue from "./pages/SetValue";
6 |
7 | Vue.use(Router);
8 |
9 | export default new Router({
10 | // Make sure the server can handle the history mode
11 | // If not, set it to hash (or delete the mode)
12 | // More info here: https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations
13 | mode: "history",
14 | routes: [
15 | {
16 | path: "/",
17 | name: "home",
18 | component: Home
19 | },
20 | {
21 | path: "/set-value",
22 | name: "setValue",
23 | component: SetValue
24 | },
25 | {
26 | path: "/profile",
27 | name: "profile",
28 | component: Profile
29 | }
30 | ],
31 | linkActiveClass: "active"
32 | });
--------------------------------------------------------------------------------
/frontend/src/pages/Profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Your profile
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{ getActiveAccount }}
14 |
Your ETH balance: {{ Number(getActiveBalanceEth).toFixed(4) }} ETH
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
37 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@burner-wallet/burner-connect-provider": "^0.1.1",
12 | "authereum": "^0.1.0",
13 | "core-js": "^3.6.5",
14 | "ethers": "^5.0.24",
15 | "vue": "^2.6.11",
16 | "vue-gravatar": "^1.3.1",
17 | "vue-router": "^3.4.9",
18 | "vue-toasted": "^1.1.28",
19 | "vuex": "^3.6.0",
20 | "web3modal": "^1.9.2"
21 | },
22 | "devDependencies": {
23 | "@vue/cli-plugin-babel": "~4.5.0",
24 | "@vue/cli-plugin-eslint": "~4.5.0",
25 | "@vue/cli-service": "~4.5.0",
26 | "babel-eslint": "^10.1.0",
27 | "eslint": "^6.7.2",
28 | "eslint-plugin-vue": "^6.2.2",
29 | "vue-template-compiler": "^2.6.11"
30 | },
31 | "eslintConfig": {
32 | "root": true,
33 | "env": {
34 | "node": true
35 | },
36 | "extends": [
37 | "plugin:vue/essential",
38 | "eslint:recommended"
39 | ],
40 | "parserOptions": {
41 | "parser": "babel-eslint"
42 | },
43 | "rules": {}
44 | },
45 | "browserslist": [
46 | "> 1%",
47 | "last 2 versions",
48 | "not dead"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/frontend/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
36 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/contracts.js:
--------------------------------------------------------------------------------
1 | import { ethers } from "ethers";
2 | import Calc from "../../contracts/Calc.json";
3 | import addresses from "../../contracts/addresses.json";
4 |
5 | const state = {
6 | num: 0,
7 | calcAbi: null,
8 | calcAddress: null
9 | };
10 |
11 | const getters = {
12 | getNum(state) {
13 | return state.num;
14 | },
15 | getCalcAbi(state) {
16 | return state.calcAbi;
17 | },
18 | getCalcAddress(state) {
19 | return state.calcAddress;
20 | }
21 | };
22 |
23 | const actions = {
24 | async fetchNum({ commit, rootState }) {
25 | let provider = rootState.accounts.providerEthers;
26 | let chainIdDec = parseInt(rootState.accounts.chainId);
27 | let calcAddress = addresses.Calc[chainIdDec];
28 |
29 | let contract = new ethers.Contract(calcAddress, Calc.abi, provider);
30 |
31 | let num = await contract.getNum();
32 |
33 | commit("setNum", num);
34 | },
35 | storeCalcAbi({commit}) {
36 | commit("setCalcAbi", Calc.abi);
37 | },
38 | storeCalcAddress({ commit, rootState }) {
39 | let chainIdDec = parseInt(rootState.accounts.chainId);
40 | let calcAddress = addresses.Calc[chainIdDec];
41 |
42 | commit("setCalcAddress", calcAddress);
43 | }
44 | };
45 |
46 | const mutations = {
47 | setNum(state, _num) {
48 | state.num = _num;
49 | },
50 | setCalcAbi(state, abi) {
51 | state.calcAbi = abi;
52 | },
53 | setCalcAddress(state, address) {
54 | state.calcAddress = address;
55 | }
56 | };
57 |
58 | export default {
59 | namespaced: true,
60 | state,
61 | getters,
62 | actions,
63 | mutations
64 | };
65 |
--------------------------------------------------------------------------------
/frontend/public/bootstrap/dashboard.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: .875rem;
3 | }
4 |
5 | .feather {
6 | width: 16px;
7 | height: 16px;
8 | vertical-align: text-bottom;
9 | }
10 |
11 | /*
12 | * Sidebar
13 | */
14 |
15 | .sidebar {
16 | position: fixed;
17 | top: 0;
18 | /* rtl:raw:
19 | right: 0;
20 | */
21 | bottom: 0;
22 | /* rtl:remove */
23 | left: 0;
24 | z-index: 100; /* Behind the navbar */
25 | padding: 48px 0 0; /* Height of navbar */
26 | box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
27 | }
28 |
29 | @media (max-width: 767.98px) {
30 | .sidebar {
31 | top: 5rem;
32 | }
33 | }
34 |
35 | .sidebar-sticky {
36 | position: relative;
37 | top: 0;
38 | height: calc(100vh - 48px);
39 | padding-top: .5rem;
40 | overflow-x: hidden;
41 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
42 | }
43 |
44 | .sidebar .nav-link {
45 | font-weight: 500;
46 | color: #333;
47 | }
48 |
49 | .sidebar .nav-link .feather {
50 | margin-right: 4px;
51 | color: #727272;
52 | }
53 |
54 | .sidebar .nav-link.active {
55 | color: #007bff;
56 | }
57 |
58 | .sidebar .nav-link:hover .feather,
59 | .sidebar .nav-link.active .feather {
60 | color: inherit;
61 | }
62 |
63 | .sidebar-heading {
64 | font-size: .75rem;
65 | text-transform: uppercase;
66 | }
67 |
68 | /*
69 | * Navbar
70 | */
71 |
72 | .navbar-brand {
73 | padding-top: .75rem;
74 | padding-bottom: .75rem;
75 | font-size: 1rem;
76 | background-color: rgba(0, 0, 0, .25);
77 | box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
78 | }
79 |
80 | .navbar .navbar-toggler {
81 | top: .25rem;
82 | right: 1rem;
83 | }
84 |
85 | .navbar .form-control {
86 | padding: .75rem 1rem;
87 | border-width: 0;
88 | border-radius: 0;
89 | }
90 |
91 | .form-control-dark {
92 | color: #fff;
93 | background-color: rgba(255, 255, 255, .1);
94 | border-color: rgba(255, 255, 255, .1);
95 | }
96 |
97 | .form-control-dark:focus {
98 | border-color: transparent;
99 | box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
100 | }
101 |
--------------------------------------------------------------------------------
/frontend/src/contracts/Calc.json:
--------------------------------------------------------------------------------
1 | {
2 | "_format": "hh-sol-artifact-1",
3 | "contractName": "Calc",
4 | "sourceName": "contracts/Calc.sol",
5 | "abi": [
6 | {
7 | "anonymous": false,
8 | "inputs": [
9 | {
10 | "indexed": false,
11 | "internalType": "address",
12 | "name": "_from",
13 | "type": "address"
14 | },
15 | {
16 | "indexed": false,
17 | "internalType": "uint256",
18 | "name": "value",
19 | "type": "uint256"
20 | }
21 | ],
22 | "name": "NumberSet",
23 | "type": "event"
24 | },
25 | {
26 | "inputs": [],
27 | "name": "getNum",
28 | "outputs": [
29 | {
30 | "internalType": "uint256",
31 | "name": "",
32 | "type": "uint256"
33 | }
34 | ],
35 | "stateMutability": "view",
36 | "type": "function"
37 | },
38 | {
39 | "inputs": [
40 | {
41 | "internalType": "uint256",
42 | "name": "_num",
43 | "type": "uint256"
44 | }
45 | ],
46 | "name": "setNum",
47 | "outputs": [],
48 | "stateMutability": "nonpayable",
49 | "type": "function"
50 | }
51 | ],
52 | "bytecode": "0x60806040526000805534801561001457600080fd5b5060ea806100236000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806367e0badb146037578063cd16ecbf14604f575b600080fd5b603d606b565b60408051918252519081900360200190f35b606960048036036020811015606357600080fd5b50356071565b005b60005490565b6000819055604080513381526020810183905281517fdf1b3bea69f5a320d08715bed3eaa0ac393a2ecb2b5dca3f8c3c5f8ec8f575e4929181900390910190a15056fea2646970667358221220a043846772f09386d05d22bd298bbcbd1095df2fa5caaadffeec2c58649da0e364736f6c63430007030033",
53 | "deployedBytecode": "0x6080604052348015600f57600080fd5b506004361060325760003560e01c806367e0badb146037578063cd16ecbf14604f575b600080fd5b603d606b565b60408051918252519081900360200190f35b606960048036036020811015606357600080fd5b50356071565b005b60005490565b6000819055604080513381526020810183905281517fdf1b3bea69f5a320d08715bed3eaa0ac393a2ecb2b5dca3f8c3c5f8ec8f575e4929181900390910190a15056fea2646970667358221220a043846772f09386d05d22bd298bbcbd1095df2fa5caaadffeec2c58649da0e364736f6c63430007030033",
54 | "linkReferences": {},
55 | "deployedLinkReferences": {}
56 | }
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hardhat Vue.js Starter Template
2 |
3 | A starter template for Ethereum dApps that uses the following tools:
4 |
5 | - Hardhat
6 | - Waffle
7 | - ethers.js
8 | - Vue & Vuex
9 | - Web3Modal
10 | - Bootstrap 5
11 | - Vue Toasted
12 | - Vue Gravatar
13 |
14 | ## Features
15 |
16 | ### Seamless connect/disconnect wallet experience (using Web3Modal)
17 |
18 | Web3Modal is used to support various different Ethereum wallets. When user switches between accounts and even chains, the UI quickly notices that and adapts to the change (account and ETH balance data are refreshed).
19 |
20 | 
21 |
22 | ### Alert when not on mainnet
23 |
24 | If user's wallet is not set to Mainnet, an unobtrusive yellow alert band shows up just above the navigation bar. The alert notifies the user which (testnet) chain they are currently using.
25 |
26 | 
27 |
28 | ### Storing contract addresses and ABIs on front-end
29 |
30 | The deploy.js script automatically stores all contract ABIs and their respective addresses in the /frontend/src/contracts folder.
31 |
32 | Addresses are separated from one another per contract name and also per chain ID.
33 |
34 | Example (`addresses.json`):
35 |
36 | ```json
37 | {
38 | "Token":{
39 | "1337":"0x78afecb367f032d93eDf865Ada339AFf6ef2621b",
40 | "3":"0x5FbDB2315678afecb367f032d93F642f64180aa3",
41 | "1":"0xE2Df865998BD3f20117e037d1293367f032d93F6"
42 | },
43 | "Farm":{
44 | "1337":"0x1Cf865998BD3f20eB6BCdAda339aa8BD3f2e26eb",
45 | "3":"0x998BD3f20eB6Bafecb3673f201ca17e037d10aa3",
46 | "1":"0xBCdAda33b67815678afecb365998BD3f2e26BCdA"
47 | }
48 | }
49 | ```
50 |
51 | ## npm install
52 |
53 | Run installations in both root and in the frontend folder:
54 |
55 | ```bash
56 | npm install
57 | cd frontend && npm install
58 | ```
59 |
60 | ## Run Vue app
61 |
62 | ```bash
63 | cd frontend && npm run serve
64 | ```
65 |
66 | ## Tests
67 |
68 | ### Solidity/Hardhat
69 |
70 | ```bash
71 | npx hardhat test
72 | ```
73 |
74 | ## Deployment to ganache
75 |
76 | ```bash
77 | npx hardhat run scripts/deploy.js --network ganache
78 | ```
79 |
80 | ## Deployment to a remote blockchain
81 |
82 | ```bash
83 | npx hardhat run scripts/deploy.js --network goerli
84 | ```
85 |
86 | ## Verify on Etherscan
87 |
88 | ```bash
89 | npx hardhat --network mainnet etherscan-verify --api-key
90 | ```
91 |
--------------------------------------------------------------------------------
/scripts/deploy.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // write down contracts that you wish to deploy one-by-one (names only, no .sol extension)
5 | // after the run, find the ABIs and addresses in frontend/src/contracts
6 | const contracts = ["Calc"];
7 |
8 | // DO NOT MODIFY CODE BELOW UNLESS ABSOLUTELY NECESSARY
9 | async function publishContract(contractName, chainId) {
10 | // deploy the contract
11 | const contractFactory = await ethers.getContractFactory(contractName);
12 | const contract = await contractFactory.deploy();
13 |
14 | console.log(contractName + " contract address: " + contract.address);
15 |
16 | // copy the contract JSON file to front-end and add the address field in it
17 | fs.copyFileSync(
18 | path.join(__dirname, "../artifacts/contracts/" + contractName + ".sol/" + contractName + ".json"), //source
19 | path.join(__dirname, "../frontend/src/contracts/" + contractName + ".json") // destination
20 | );
21 |
22 | // check if addresses.json already exists
23 | let exists = fs.existsSync(path.join(__dirname, "../frontend/src/contracts/addresses.json"));
24 |
25 | // if not, created the file
26 | if (!exists) {
27 | fs.writeFileSync(
28 | path.join(__dirname, "../frontend/src/contracts/addresses.json"),
29 | "{}"
30 | );
31 | }
32 |
33 | // update the addresses.json file with the new contract address
34 | let addressesFile = fs.readFileSync(path.join(__dirname, "../frontend/src/contracts/addresses.json"));
35 | let addressesJson = JSON.parse(addressesFile);
36 |
37 | if (!addressesJson[contractName]) {
38 | addressesJson[contractName] = {};
39 | }
40 |
41 | addressesJson[contractName][chainId] = contract.address;
42 |
43 | fs.writeFileSync(
44 | path.join(__dirname, "../frontend/src/contracts/addresses.json"),
45 | JSON.stringify(addressesJson)
46 | );
47 | }
48 |
49 | async function main() {
50 | const [deployer] = await ethers.getSigners();
51 |
52 | let networkData = await deployer.provider.getNetwork()
53 | console.log("Chain ID:", networkData.chainId);
54 |
55 | console.log(
56 | "Deploying contracts with the account:",
57 | deployer.address
58 | );
59 |
60 | console.log("Account balance:", (await deployer.getBalance()).toString());
61 |
62 | for (cont of contracts) {
63 | await publishContract(cont, networkData.chainId);
64 | }
65 | }
66 |
67 | main()
68 | .then(() => process.exit(0))
69 | .catch(error => {
70 | console.error(error);
71 | process.exit(1);
72 | });
73 |
--------------------------------------------------------------------------------
/frontend/src/components/Sidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
47 |
48 |
49 |
75 |
--------------------------------------------------------------------------------
/frontend/src/pages/SetValue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Set a new value
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Current value
12 |
13 |
14 | Value: {{ getNum }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Set new value
27 |
28 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/accounts.js:
--------------------------------------------------------------------------------
1 | import Web3Modal from "web3modal";
2 | import { ethers } from "ethers";
3 | import BurnerConnectProvider from "@burner-wallet/burner-connect-provider";
4 | import Authereum from "authereum";
5 |
6 | const state = {
7 | activeAccount: null,
8 | activeBalance: 0,
9 | chainId: null,
10 | chainName: null,
11 | providerEthers: null, // this is "provider" for Ethers.js
12 | isConnected: false,
13 | providerW3m: null, // this is "provider" from Web3Modal
14 | web3Modal: null
15 | };
16 |
17 | const getters = {
18 | getActiveAccount(state) {
19 | return state.activeAccount;
20 | },
21 | getActiveBalanceWei(state) {
22 | return state.activeBalance;
23 | },
24 | getActiveBalanceEth(state) {
25 | return ethers.utils.formatEther(state.activeBalance);
26 | },
27 | getChainId(state) {
28 | return state.chainId;
29 | },
30 | getChainName(state) {
31 | return state.chainName;
32 | },
33 | getProviderEthers(state) {
34 | return state.providerEthers;
35 | },
36 | getWeb3Modal(state) {
37 | return state.web3Modal;
38 | },
39 | isUserConnected(state) {
40 | return state.isConnected;
41 | }
42 | };
43 |
44 | const actions = {
45 |
46 | async initWeb3Modal({ commit }) {
47 | const providerOptions = {
48 | // MetaMask is enabled by default
49 | // Find other providers here: https://github.com/Web3Modal/web3modal/tree/master/docs/providers
50 | burnerconnect: {
51 | package: BurnerConnectProvider // required
52 | },
53 | authereum: {
54 | package: Authereum // required
55 | }
56 | };
57 |
58 | const w3mObject = new Web3Modal({
59 | cacheProvider: true, // optional
60 | providerOptions // required
61 | });
62 |
63 | // This will get deprecated soon. Setting it to false removes a warning from the console.
64 | window.ethereum.autoRefreshOnNetworkChange = false;
65 |
66 | // if the user is flagged as already connected, automatically connect back to Web3Modal
67 | if (localStorage.getItem('isConnected') === "true") {
68 | let providerW3m = await w3mObject.connect();
69 | commit("setIsConnected", true);
70 |
71 | commit("setActiveAccount", window.ethereum.selectedAddress);
72 | commit("setChainData", window.ethereum.chainId);
73 | commit("setEthersProvider", providerW3m);
74 | actions.fetchActiveBalance({ commit });
75 | }
76 |
77 | commit("setWeb3ModalInstance", w3mObject);
78 | },
79 |
80 | async connectWeb3Modal({ commit }) {
81 | let providerW3m = await state.web3Modal.connect();
82 | commit("setIsConnected", true);
83 |
84 | commit("setActiveAccount", window.ethereum.selectedAddress);
85 | commit("setChainData", window.ethereum.chainId);
86 | commit("setEthersProvider", providerW3m);
87 | actions.fetchActiveBalance({ commit });
88 | },
89 |
90 | async disconnectWeb3Modal({ commit }) {
91 | commit("disconnectWallet");
92 | commit("setIsConnected", false);
93 | },
94 |
95 | async ethereumListener({ commit }) {
96 |
97 | window.ethereum.on('accountsChanged', (accounts) => {
98 | if (state.isConnected) {
99 | commit("setActiveAccount", accounts[0]);
100 | commit("setEthersProvider", state.providerW3m);
101 | actions.fetchActiveBalance({ commit });
102 | }
103 | });
104 |
105 | window.ethereum.on('chainChanged', (chainId) => {
106 | commit("setChainData", chainId);
107 | commit("setEthersProvider", state.providerW3m);
108 | actions.fetchActiveBalance({ commit });
109 | });
110 |
111 | },
112 |
113 | async fetchActiveBalance({ commit }) {
114 | let balance = await state.providerEthers.getBalance(state.activeAccount);
115 | commit("setActiveBalance", balance);
116 | }
117 |
118 | };
119 |
120 | const mutations = {
121 |
122 | async disconnectWallet(state) {
123 | state.activeAccount = null;
124 | state.activeBalance = 0;
125 | state.providerEthers = null;
126 | if (state.providerW3m.close && state.providerW3m !== null) {
127 | await state.providerW3m.close();
128 | }
129 | state.providerW3m = null;
130 | await state.web3Modal.clearCachedProvider();
131 |
132 | window.location.href = '../'; // redirect to the Main page
133 | },
134 |
135 | setActiveAccount(state, selectedAddress) {
136 | state.activeAccount = selectedAddress;
137 | },
138 |
139 | setActiveBalance(state, balance) {
140 | state.activeBalance = balance;
141 | },
142 |
143 | setChainData(state, chainId) {
144 | state.chainId = chainId;
145 |
146 | switch(chainId) {
147 | case "0x1":
148 | state.chainName = "Mainnet";
149 | break;
150 | case "0x2a":
151 | state.chainName = "Kovan";
152 | break;
153 | case "0x3":
154 | state.chainName = "Ropsten";
155 | break;
156 | case "0x4":
157 | state.chainName = "Rinkeby";
158 | break;
159 | case "0x5":
160 | state.chainName = "Goerli";
161 | break;
162 | case "0x539": // 1337 (often used on localhost)
163 | case "0x1691": // 5777 (default in Ganache)
164 | default:
165 | state.chainName = "Localhost";
166 | break;
167 | }
168 | },
169 |
170 | async setEthersProvider(state, providerW3m) {
171 | state.providerW3m = providerW3m;
172 | state.providerEthers = new ethers.providers.Web3Provider(providerW3m);
173 | },
174 |
175 | setIsConnected(state, isConnected) {
176 | state.isConnected = isConnected;
177 | // add to persistent storage so that the user can be logged back in when revisiting website
178 | localStorage.setItem('isConnected', isConnected);
179 | },
180 |
181 | setWeb3ModalInstance(state, w3mObject) {
182 | state.web3Modal = w3mObject;
183 | }
184 |
185 | };
186 |
187 | export default {
188 | namespaced: true,
189 | state,
190 | getters,
191 | actions,
192 | mutations
193 | };
194 |
--------------------------------------------------------------------------------
/frontend/public/bootstrap/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v5.0.0-beta1 (https://getbootstrap.com/)
3 | * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5 | */
6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e(t.Popper)}(this,(function(t){"use strict";function e(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach((function(n){if("default"!==n){var i=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,i.get?i:{enumerable:!0,get:function(){return t[n]}})}})),e.default=t,Object.freeze(e)}var n=e(t);function i(t,e){for(var n=0;n0,i._pointerEvent=Boolean(window.PointerEvent),i._addEventListeners(),i}r(e,t);var n=e.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&v(this._element)&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),V.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(p(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var e=this;this._activeElement=V.findOne(".active.carousel-item",this._element);var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)Q.one(this._element,"slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?"next":"prev";this._slide(i,this._items[t])}},n.dispose=function(){t.prototype.dispose.call(this),Q.off(this._element,G),this._items=null,this._config=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=s({},Z,t),_($,t,J),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&Q.on(this._element,"keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&(Q.on(this._element,"mouseenter.bs.carousel",(function(e){return t.pause(e)})),Q.on(this._element,"mouseleave.bs.carousel",(function(e){return t.cycle(e)}))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this,e=function(e){t._pointerEvent&&tt[e.pointerType.toUpperCase()]?t.touchStartX=e.clientX:t._pointerEvent||(t.touchStartX=e.touches[0].clientX)},n=function(e){t._pointerEvent&&tt[e.pointerType.toUpperCase()]&&(t.touchDeltaX=e.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};V.find(".carousel-item img",this._element).forEach((function(t){Q.on(t,"dragstart.bs.carousel",(function(t){return t.preventDefault()}))})),this._pointerEvent?(Q.on(this._element,"pointerdown.bs.carousel",(function(t){return e(t)})),Q.on(this._element,"pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(Q.on(this._element,"touchstart.bs.carousel",(function(t){return e(t)})),Q.on(this._element,"touchmove.bs.carousel",(function(e){return function(e){e.touches&&e.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.touches[0].clientX-t.touchStartX}(e)})),Q.on(this._element,"touchend.bs.carousel",(function(t){return n(t)})))},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.key){case"ArrowLeft":t.preventDefault(),this.prev();break;case"ArrowRight":t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?V.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(V.findOne(".active.carousel-item",this._element));return Q.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n})},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){for(var e=V.find(".active",this._indicatorsElement),n=0;n0)for(var i=0;i0&&s--,"ArrowDown"===t.key&&sdocument.documentElement.clientHeight;e||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var n=h(this._dialog);Q.off(this._element,"transitionend"),Q.one(this._element,"transitionend",(function(){t._element.classList.remove("modal-static"),e||(Q.one(t._element,"transitionend",(function(){t._element.style.overflowY=""})),m(t._element,n))})),m(this._element,n),this._element.focus()}},n._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;(!this._isBodyOverflowing&&t&&!T||this._isBodyOverflowing&&!t&&T)&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),(this._isBodyOverflowing&&!t&&!T||!this._isBodyOverflowing&&t&&T)&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",container:!1,fallbackPlacements:null,boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:Tt,popperConfig:null},Ot={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},It=function(e){function i(t,i){var o;if(void 0===n)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");return(o=e.call(this,t)||this)._isEnabled=!0,o._timeout=0,o._hoverState="",o._activeTrigger={},o._popper=null,o.config=o._getConfig(i),o.tip=null,o._setListeners(),o}r(i,e);var a=i.prototype;return a.enable=function(){this._isEnabled=!0},a.disable=function(){this._isEnabled=!1},a.toggleEnabled=function(){this._isEnabled=!this._isEnabled},a.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=L(t.delegateTarget,e);n||(n=new this.constructor(t.delegateTarget,this._getDelegateConfig()),A(t.delegateTarget,e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(this.getTipElement().classList.contains("show"))return void this._leave(null,this);this._enter(null,this)}},a.dispose=function(){clearTimeout(this._timeout),Q.off(this._element,this.constructor.EVENT_KEY),Q.off(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this.tip&&this.tip.parentNode.removeChild(this.tip),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.config=null,this.tip=null,e.prototype.dispose.call(this)},a.show=function(){var e=this;if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(this.isWithContent()&&this._isEnabled){var n=Q.trigger(this._element,this.constructor.Event.SHOW),i=function t(e){if(!document.documentElement.attachShadow)return null;if("function"==typeof e.getRootNode){var n=e.getRootNode();return n instanceof ShadowRoot?n:null}return e instanceof ShadowRoot?e:e.parentNode?t(e.parentNode):null}(this._element),o=null===i?this._element.ownerDocument.documentElement.contains(this._element):i.contains(this._element);if(n.defaultPrevented||!o)return;var s=this.getTipElement(),r=c(this.constructor.NAME);s.setAttribute("id",r),this._element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&s.classList.add("fade");var a="function"==typeof this.config.placement?this.config.placement.call(this,s,this._element):this.config.placement,l=this._getAttachment(a);this._addAttachmentClass(l);var u=this._getContainer();A(s,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||u.appendChild(s),Q.trigger(this._element,this.constructor.Event.INSERTED),this._popper=t.createPopper(this._element,s,this._getPopperConfig(l)),s.classList.add("show");var d,f,p="function"==typeof this.config.customClass?this.config.customClass():this.config.customClass;if(p)(d=s.classList).add.apply(d,p.split(" "));if("ontouchstart"in document.documentElement)(f=[]).concat.apply(f,document.body.children).forEach((function(t){Q.on(t,"mouseover",(function(){}))}));var g=function(){var t=e._hoverState;e._hoverState=null,Q.trigger(e._element,e.constructor.Event.SHOWN),"out"===t&&e._leave(null,e)};if(this.tip.classList.contains("fade")){var _=h(this.tip);Q.one(this.tip,"transitionend",g),m(this.tip,_)}else g()}},a.hide=function(){var t=this;if(this._popper){var e=this.getTipElement(),n=function(){"show"!==t._hoverState&&e.parentNode&&e.parentNode.removeChild(e),t._cleanTipClass(),t._element.removeAttribute("aria-describedby"),Q.trigger(t._element,t.constructor.Event.HIDDEN),t._popper&&(t._popper.destroy(),t._popper=null)};if(!Q.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented){var i;if(e.classList.remove("show"),"ontouchstart"in document.documentElement)(i=[]).concat.apply(i,document.body.children).forEach((function(t){return Q.off(t,"mouseover",b)}));if(this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this.tip.classList.contains("fade")){var o=h(e);Q.one(e,"transitionend",n),m(e,o)}else n();this._hoverState=""}}},a.update=function(){null!==this._popper&&this._popper.update()},a.isWithContent=function(){return Boolean(this.getTitle())},a.getTipElement=function(){if(this.tip)return this.tip;var t=document.createElement("div");return t.innerHTML=this.config.template,this.tip=t.children[0],this.tip},a.setContent=function(){var t=this.getTipElement();this.setElementContent(V.findOne(".tooltip-inner",t),this.getTitle()),t.classList.remove("fade","show")},a.setElementContent=function(t,e){if(null!==t)return"object"==typeof e&&g(e)?(e.jquery&&(e=e[0]),void(this.config.html?e.parentNode!==t&&(t.innerHTML="",t.appendChild(e)):t.textContent=e.textContent)):void(this.config.html?(this.config.sanitize&&(e=kt(e,this.config.allowList,this.config.sanitizeFn)),t.innerHTML=e):t.textContent=e)},a.getTitle=function(){var t=this._element.getAttribute("data-bs-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this._element):this.config.title),t},a.updateAttachment=function(t){return"right"===t?"end":"left"===t?"start":t},a._getPopperConfig=function(t){var e=this,n={name:"flip",options:{altBoundary:!0}};return this.config.fallbackPlacements&&(n.options.fallbackPlacements=this.config.fallbackPlacements),s({},{placement:t,modifiers:[n,{name:"preventOverflow",options:{rootBoundary:this.config.boundary}},{name:"arrow",options:{element:"."+this.constructor.NAME+"-arrow"}},{name:"onChange",enabled:!0,phase:"afterWrite",fn:function(t){return e._handlePopperPlacementChange(t)}}],onFirstUpdate:function(t){t.options.placement!==t.placement&&e._handlePopperPlacementChange(t)}},this.config.popperConfig)},a._addAttachmentClass=function(t){this.getTipElement().classList.add("bs-tooltip-"+this.updateAttachment(t))},a._getContainer=function(){return!1===this.config.container?document.body:g(this.config.container)?this.config.container:V.findOne(this.config.container)},a._getAttachment=function(t){return St[t.toUpperCase()]},a._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)Q.on(t._element,t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;Q.on(t._element,n,t.config.selector,(function(e){return t._enter(e)})),Q.on(t._element,i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t._element&&t.hide()},Q.on(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=s({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},a._fixTitle=function(){var t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))},a._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||L(t.delegateTarget,n))||(e=new this.constructor(t.delegateTarget,this._getDelegateConfig()),A(t.delegateTarget,n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e.getTipElement().classList.contains("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},a._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||L(t.delegateTarget,n))||(e=new this.constructor(t.delegateTarget,this._getDelegateConfig()),A(t.delegateTarget,n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},a._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},a._getConfig=function(t){var e=q.getDataAttributes(this._element);return Object.keys(e).forEach((function(t){Ct.has(t)&&delete e[t]})),t&&"object"==typeof t.container&&t.container.jquery&&(t.container=t.container[0]),"number"==typeof(t=s({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_(At,t,this.constructor.DefaultType),t.sanitize&&(t.template=kt(t.template,t.allowList,t.sanitizeFn)),t},a._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},a._cleanTipClass=function(){var t=this.getTipElement(),e=t.getAttribute("class").match(Lt);null!==e&&e.length>0&&e.map((function(t){return t.trim()})).forEach((function(e){return t.classList.remove(e)}))},a._handlePopperPlacementChange=function(t){var e=t.state;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))},i.jQueryInterface=function(t){return this.each((function(){var e=L(this,"bs.tooltip"),n="object"==typeof t&&t;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n)),"string"==typeof t)){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},o(i,null,[{key:"Default",get:function(){return Nt}},{key:"NAME",get:function(){return At}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return Ot}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return Dt}}]),i}(U);E((function(){var t=w();if(t){var e=t.fn[At];t.fn[At]=It.jQueryInterface,t.fn[At].Constructor=It,t.fn[At].noConflict=function(){return t.fn[At]=e,It.jQueryInterface}}}));var jt="popover",Pt=new RegExp("(^|\\s)bs-popover\\S+","g"),xt=s({},It.Default,{placement:"right",trigger:"click",content:"",template:''}),Ht=s({},It.DefaultType,{content:"(string|element|function)"}),Bt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},Mt=function(t){function e(){return t.apply(this,arguments)||this}r(e,t);var n=e.prototype;return n.isWithContent=function(){return this.getTitle()||this._getContent()},n.setContent=function(){var t=this.getTipElement();this.setElementContent(V.findOne(".popover-header",t),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this._element)),this.setElementContent(V.findOne(".popover-body",t),e),t.classList.remove("fade","show")},n._addAttachmentClass=function(t){this.getTipElement().classList.add("bs-popover-"+this.updateAttachment(t))},n._getContent=function(){return this._element.getAttribute("data-bs-content")||this.config.content},n._cleanTipClass=function(){var t=this.getTipElement(),e=t.getAttribute("class").match(Pt);null!==e&&e.length>0&&e.map((function(t){return t.trim()})).forEach((function(e){return t.classList.remove(e)}))},e.jQueryInterface=function(t){return this.each((function(){var n=L(this,"bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new e(this,i),A(this,"bs.popover",n)),"string"==typeof t)){if(void 0===n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(e,null,[{key:"Default",get:function(){return xt}},{key:"NAME",get:function(){return jt}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return Bt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Ht}}]),e}(It);E((function(){var t=w();if(t){var e=t.fn[jt];t.fn[jt]=Mt.jQueryInterface,t.fn[jt].Constructor=Mt,t.fn[jt].noConflict=function(){return t.fn[jt]=e,Mt.jQueryInterface}}}));var Rt="scrollspy",Kt={offset:10,method:"auto",target:""},Qt={offset:"number",method:"string",target:"(string|element)"},Ut=function(t){function e(e,n){var i;return(i=t.call(this,e)||this)._scrollElement="BODY"===e.tagName?window:e,i._config=i._getConfig(n),i._selector=i._config.target+" .nav-link, "+i._config.target+" .list-group-item, "+i._config.target+" .dropdown-item",i._offsets=[],i._targets=[],i._activeTarget=null,i._scrollHeight=0,Q.on(i._scrollElement,"scroll.bs.scrollspy",(function(t){return i._process(t)})),i.refresh(),i._process(),i}r(e,t);var n=e.prototype;return n.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,i="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),V.find(this._selector).map((function(t){var e=d(t),o=e?V.findOne(e):null;if(o){var s=o.getBoundingClientRect();if(s.width||s.height)return[q[n](o).top+i,e]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){t.prototype.dispose.call(this),Q.off(this._scrollElement,".bs.scrollspy"),this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=s({},Kt,"object"==typeof t&&t?t:{})).target&&g(t.target)){var e=t.target.id;e||(e=c(Rt),t.target.id=e),t.target="#"+e}return _(Rt,t,Qt),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&(void 0===this._offsets[o+1]||t li > .active":".active";e=(e=V.find(o,i))[e.length-1]}var s=null;if(e&&(s=Q.trigger(e,"hide.bs.tab",{relatedTarget:this._element})),!(Q.trigger(this._element,"show.bs.tab",{relatedTarget:e}).defaultPrevented||null!==s&&s.defaultPrevented)){this._activate(this._element,i);var r=function(){Q.trigger(e,"hidden.bs.tab",{relatedTarget:t._element}),Q.trigger(t._element,"shown.bs.tab",{relatedTarget:e})};n?this._activate(n,n.parentNode,r):r()}}},n._activate=function(t,e,n){var i=this,o=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?V.children(e,".active"):V.find(":scope > li > .active",e))[0],s=n&&o&&o.classList.contains("fade"),r=function(){return i._transitionComplete(t,o,n)};if(o&&s){var a=h(o);o.classList.remove("show"),Q.one(o,"transitionend",r),m(o,a)}else r()},n._transitionComplete=function(t,e,n){if(e){e.classList.remove("active");var i=V.findOne(":scope > .dropdown-menu .active",e.parentNode);i&&i.classList.remove("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}(t.classList.add("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),y(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&t.parentNode.classList.contains("dropdown-menu"))&&(t.closest(".dropdown")&&V.find(".dropdown-toggle").forEach((function(t){return t.classList.add("active")})),t.setAttribute("aria-expanded",!0));n&&n()},e.jQueryInterface=function(t){return this.each((function(){var n=L(this,"bs.tab")||new e(this);if("string"==typeof t){if(void 0===n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(e,null,[{key:"DATA_KEY",get:function(){return"bs.tab"}}]),e}(U);Q.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){t.preventDefault(),(L(this,"bs.tab")||new Wt(this)).show()})),E((function(){var t=w();if(t){var e=t.fn.tab;t.fn.tab=Wt.jQueryInterface,t.fn.tab.Constructor=Wt,t.fn.tab.noConflict=function(){return t.fn.tab=e,Wt.jQueryInterface}}}));var Ft={animation:"boolean",autohide:"boolean",delay:"number"},Yt={animation:!0,autohide:!0,delay:5e3},zt=function(t){function e(e,n){var i;return(i=t.call(this,e)||this)._config=i._getConfig(n),i._timeout=null,i._setListeners(),i}r(e,t);var n=e.prototype;return n.show=function(){var t=this;if(!Q.trigger(this._element,"show.bs.toast").defaultPrevented){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var e=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),Q.trigger(t._element,"shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),y(this._element),this._element.classList.add("showing"),this._config.animation){var n=h(this._element);Q.one(this._element,"transitionend",e),m(this._element,n)}else e()}},n.hide=function(){var t=this;if(this._element.classList.contains("show")&&!Q.trigger(this._element,"hide.bs.toast").defaultPrevented){var e=function(){t._element.classList.add("hide"),Q.trigger(t._element,"hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=h(this._element);Q.one(this._element,"transitionend",e),m(this._element,n)}else e()}},n.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),Q.off(this._element,"click.dismiss.bs.toast"),t.prototype.dispose.call(this),this._config=null},n._getConfig=function(t){return t=s({},Yt,q.getDataAttributes(this._element),"object"==typeof t&&t?t:{}),_("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;Q.on(this._element,"click.dismiss.bs.toast",'[data-bs-dismiss="toast"]',(function(){return t.hide()}))},n._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},e.jQueryInterface=function(t){return this.each((function(){var n=L(this,"bs.toast");if(n||(n=new e(this,"object"==typeof t&&t)),"string"==typeof t){if(void 0===n[t])throw new TypeError('No method named "'+t+'"');n[t](this)}}))},o(e,null,[{key:"DefaultType",get:function(){return Ft}},{key:"Default",get:function(){return Yt}},{key:"DATA_KEY",get:function(){return"bs.toast"}}]),e}(U);return E((function(){var t=w();if(t){var e=t.fn.toast;t.fn.toast=zt.jQueryInterface,t.fn.toast.Constructor=zt,t.fn.toast.noConflict=function(){return t.fn.toast=e,zt.jQueryInterface}}})),{Alert:F,Button:Y,Carousel:et,Collapse:st,Dropdown:mt,Modal:bt,Popover:Mt,ScrollSpy:Ut,Tab:Wt,Toast:zt,Tooltip:It}}));
7 | //# sourceMappingURL=bootstrap.min.js.map
--------------------------------------------------------------------------------