feat: management page
This commit is contained in:
parent
caa854620a
commit
56ef570249
@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
<meta name="description" content="" />
|
<meta name="description" content="" />
|
||||||
<meta name="author" content="" />
|
<meta name="author" content="" />
|
||||||
<title>My Goerli Faucet</title>
|
<title>My Sepolia Faucet</title>
|
||||||
<!-- Favicon-->
|
<!-- Favicon-->
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
<!-- Font Awesome icons (free version)-->
|
<!-- Font Awesome icons (free version)-->
|
||||||
|
|||||||
14
package-lock.json
generated
14
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@metamask/detect-provider": "^2.0.0",
|
"@metamask/detect-provider": "^2.0.0",
|
||||||
"bootstrap": "^5.2.3",
|
"bootstrap": "^5.2.3",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"web3": "^1.8.2"
|
"web3": "^1.8.2"
|
||||||
@ -1619,6 +1620,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||||
|
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ecc-jsbn": {
|
"node_modules/ecc-jsbn": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
@ -5388,6 +5397,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||||
},
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "16.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
||||||
|
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
|
||||||
|
},
|
||||||
"ecc-jsbn": {
|
"ecc-jsbn": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@metamask/detect-provider": "^2.0.0",
|
"@metamask/detect-provider": "^2.0.0",
|
||||||
"bootstrap": "^5.2.3",
|
"bootstrap": "^5.2.3",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"web3": "^1.8.2"
|
"web3": "^1.8.2"
|
||||||
|
|||||||
20
src/App.vue
20
src/App.vue
@ -3,5 +3,25 @@ import { RouterLink, RouterView } from "vue-router";
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- Navigation-->
|
||||||
|
<nav class="navbar navbar-expand-lg bg-secondary text-uppercase fixed-top" id="mainNav">
|
||||||
|
<div class="container">
|
||||||
|
<router-link class="navbar-brand" to="/">Faucet</router-link>
|
||||||
|
<button class="navbar-toggler text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
Menu
|
||||||
|
<i class="fas fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item mx-0 mx-lg-1"><router-link class="nav-link py-3 px-0 px-lg-3 rounded" to="/">Withdraw</router-link></li>
|
||||||
|
<li class="nav-item mx-0 mx-lg-1"><router-link class="nav-link py-3 px-0 px-lg-3 rounded" to="/manage">Management</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<br><br><br>
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import FaucetView from '../views/FaucetView.vue'
|
import FaucetView from '../views/FaucetView.vue'
|
||||||
|
import ManageView from '../views/ManageView.vue'
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
@ -8,6 +8,11 @@ const router = createRouter({
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: FaucetView
|
component: FaucetView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/manage',
|
||||||
|
name: 'management',
|
||||||
|
component: ManageView
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,22 +18,17 @@ export default {
|
|||||||
bankBalance: -1,
|
bankBalance: -1,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
var web3 = new Web3(window.ethereum);
|
var web3 = new Web3(window.ethereum);
|
||||||
this.bankAddr = import.meta.env.VITE_BANK_ADDR;
|
this.bankAddr = import.meta.env.VITE_BANK_ADDR;
|
||||||
var bank = new web3.eth.Contract(bankABI, this.bankAddr);
|
var bank = new web3.eth.Contract(bankABI, this.bankAddr);
|
||||||
bank.methods.getATMs().call((err, result) => {
|
var atms = await bank.methods.getATMs().call();
|
||||||
if(err){
|
this.atms = atms;
|
||||||
alert(err);
|
if(ethereum.isConnected()){
|
||||||
}else{
|
this.linked = true;
|
||||||
this.atms = result;
|
this.detectMetamask();
|
||||||
}
|
|
||||||
// console.log(result);
|
|
||||||
}).then(() => {
|
|
||||||
console.log(this.atms)
|
|
||||||
}).then(() => {
|
|
||||||
this.update();
|
this.update();
|
||||||
});
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -54,6 +49,7 @@ export default {
|
|||||||
this.atmsBalance[i] = web3.utils.fromWei(result);
|
this.atmsBalance[i] = web3.utils.fromWei(result);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
console.log("update");
|
||||||
},
|
},
|
||||||
|
|
||||||
async detectMetamask() {
|
async detectMetamask() {
|
||||||
@ -165,6 +161,7 @@ export default {
|
|||||||
<template v-for="i in atms.length">
|
<template v-for="i in atms.length">
|
||||||
<div class="divider-custom-icon">ATM {{i}} ({{atms[i-1]}}) 還有 {{ atmsBalance[i-1]}} ETH 可以提領</div>
|
<div class="divider-custom-icon">ATM {{i}} ({{atms[i-1]}}) 還有 {{ atmsBalance[i-1]}} ETH 可以提領</div>
|
||||||
</template>
|
</template>
|
||||||
|
<button v-on:click='update' class='btn btn-info'>Update</button>
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
241
src/views/ManageView.vue
Normal file
241
src/views/ManageView.vue
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
<script>
|
||||||
|
import detectEthereumProvider from '@metamask/detect-provider'
|
||||||
|
import Web3 from 'web3';
|
||||||
|
import bankABI from '@/assets/bank_abi.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ManageView',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
msg: "",
|
||||||
|
linked: false,
|
||||||
|
account: "",
|
||||||
|
owner: "",
|
||||||
|
atms: [],
|
||||||
|
atmsBalance: [],
|
||||||
|
bankAddr: "",
|
||||||
|
bankBalance: -1,
|
||||||
|
show: false,
|
||||||
|
amount: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
var web3 = new Web3(window.ethereum);
|
||||||
|
this.bankAddr = import.meta.env.VITE_BANK_ADDR;
|
||||||
|
var bank = new web3.eth.Contract(bankABI, this.bankAddr);
|
||||||
|
var owner = await bank.methods.owner().call();
|
||||||
|
this.owner = owner.toLowerCase();
|
||||||
|
var atms = await bank.methods.getATMs().call();
|
||||||
|
this.atms = atms;
|
||||||
|
if(ethereum.isConnected()){
|
||||||
|
this.linked = true;
|
||||||
|
this.detectMetamask();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
update() {
|
||||||
|
var web3 = new Web3(window.ethereum);
|
||||||
|
|
||||||
|
var contin = true;
|
||||||
|
|
||||||
|
web3.eth.getBalance(this.bankAddr, (err, result) => {
|
||||||
|
if(web3.utils.fromWei(result) != this.bankBalance)
|
||||||
|
contin = false;
|
||||||
|
this.bankBalance = web3.utils.fromWei(result);
|
||||||
|
})
|
||||||
|
for(let i=0; i<this.atms.length; i++){
|
||||||
|
web3.eth.getBalance(this.atms[i], (err, result) => {
|
||||||
|
if(web3.utils.fromWei(result) != this.atmsBalance[i])
|
||||||
|
contin = false;
|
||||||
|
this.atmsBalance[i] = web3.utils.fromWei(result);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async detectMetamask() {
|
||||||
|
this.msg = "Detecting..."
|
||||||
|
const provider = await detectEthereumProvider()
|
||||||
|
if (provider) {
|
||||||
|
this.msg = "Detect Metamask. "
|
||||||
|
const chainId = await ethereum.request({method: 'eth_chainId'})
|
||||||
|
if(chainId == 1337){
|
||||||
|
const account = await ethereum.request({ method: 'eth_requestAccounts' });
|
||||||
|
this.account = account[0];
|
||||||
|
this.msg += "> Network which you connected is Sepolia.";
|
||||||
|
this.linked = true
|
||||||
|
}else{
|
||||||
|
this.msg += "> But the network which you connected isn't Sepolia, this faucet only accept Sepolia address"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.msg = "ERROR: no Metamask"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async destroyBank() {
|
||||||
|
var web3 = new Web3(window.ethereum);
|
||||||
|
const encodeFunctionCall = web3.eth.abi.encodeFunctionCall({
|
||||||
|
name: "destroy",
|
||||||
|
type: "function",
|
||||||
|
inputs: []
|
||||||
|
}, []);
|
||||||
|
console.log(encodeFunctionCall);
|
||||||
|
const transactionParameters = {
|
||||||
|
from: ethereum.selectedAddress,
|
||||||
|
to: this.bankAddr, // smart contract's address
|
||||||
|
data: encodeFunctionCall,
|
||||||
|
value: '0x00',
|
||||||
|
};
|
||||||
|
const txHash = await ethereum.request({
|
||||||
|
method: 'eth_sendTransaction',
|
||||||
|
params: [transactionParameters]
|
||||||
|
});
|
||||||
|
console.log(txHash);
|
||||||
|
},
|
||||||
|
async destroyATM(index){
|
||||||
|
console.log(index)
|
||||||
|
var web3 = new Web3(window.ethereum);
|
||||||
|
const encodeFunctionCall = web3.eth.abi.encodeFunctionCall({
|
||||||
|
name: "destroy",
|
||||||
|
type: "function",
|
||||||
|
inputs: []
|
||||||
|
}, []);
|
||||||
|
const transactionParameters = {
|
||||||
|
from: ethereum.selectedAddress,
|
||||||
|
to: this.atms[index], // smart contract's address
|
||||||
|
data: encodeFunctionCall,
|
||||||
|
value: '0x00',
|
||||||
|
};
|
||||||
|
const txHash = await ethereum.request({
|
||||||
|
method: 'eth_sendTransaction',
|
||||||
|
params: [transactionParameters]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async transfer() {
|
||||||
|
var eth = this.amount.toString();
|
||||||
|
|
||||||
|
var web3 = new Web3(window.ethereum);
|
||||||
|
var wei = web3.utils.toWei(eth, 'ether');
|
||||||
|
const transactionParameters = {
|
||||||
|
from: ethereum.selectedAddress,
|
||||||
|
to: this.bankAddr, // smart contract's address
|
||||||
|
value: '0x'+BigInt(wei).toString(16),
|
||||||
|
};
|
||||||
|
const txHash = await ethereum.request({
|
||||||
|
method: 'eth_sendTransaction',
|
||||||
|
params: [transactionParameters]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showModel() {
|
||||||
|
if(this.show == false){
|
||||||
|
this.show = true;
|
||||||
|
}else{
|
||||||
|
this.show = false;
|
||||||
|
this.transfer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="page-section portfolio" id="portfolio">
|
||||||
|
<div class="container">
|
||||||
|
<!-- Portfolio Section Heading-->
|
||||||
|
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">Management</h2>
|
||||||
|
<!-- Icon Divider-->
|
||||||
|
<div class="divider-custom">
|
||||||
|
<div class="divider-custom-line"></div>
|
||||||
|
<div class="divider-custom-icon"><i class="fa-brands fa-ethereum"></i></div>
|
||||||
|
<div class="divider-custom-line"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Portfolio Grid Items-->
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<p>{{ msg }}</p>
|
||||||
|
<template v-if='!linked'>
|
||||||
|
<button class='btn btn-info' v-on:click="detectMetamask"> link to Metamask </button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-5">
|
||||||
|
Your address: {{ this.account }}
|
||||||
|
Bank Owner: {{ this.owner }}
|
||||||
|
<template v-if="this.account == this.owner">
|
||||||
|
<p style="color: blue">You are owner</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p style="color: red">You are not owner</p>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
<hr>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<template v-if='linked && this.account == this.owner'>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">Bank</h2>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Address</th>
|
||||||
|
<th scope="col">Balance</th>
|
||||||
|
<th scope="col">Destroy</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ bankAddr }}</td>
|
||||||
|
<td>{{ bankBalance }} ETH</td>
|
||||||
|
<td><button class="btn btn-danger" v-on:click="destroyBank">Destroy</button>
|
||||||
|
<input type="number" v-if="show" v-model="amount" placeholder="ETH">
|
||||||
|
<button class="btn btn-primary" v-on:click="showModel">Transfer</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">ATM List</h2>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Address</th>
|
||||||
|
<th scope="col">Balance</th>
|
||||||
|
<th scope="col">Destroy</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="i in atms.length">
|
||||||
|
<tr>
|
||||||
|
<td>{{ atms[i-1] }}</td>
|
||||||
|
<td>{{ atmsBalance[i-1] }} ETH</td>
|
||||||
|
<td><button class="btn btn-danger" v-on:click="destroyATM(i-1)">Destroy</button></td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br><button class='btn btn-info' v-on:click='update'>Update</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue
Block a user