mirror of
https://github.com/luxfhe/example-demo-v2.git
synced 2026-10-30 18:13:30 +00:00
614 lines
18 KiB
Vue
614 lines
18 KiB
Vue
|
|
<script lang="ts">
|
|
|
|
import FHEMixin from '../mixins/fhe';
|
|
import mainMixin from '../mixins/mainMixin';
|
|
import CommonProps from '@/mixins/CommonProps'
|
|
|
|
import { useTheme } from 'vuetify'
|
|
|
|
export default defineComponent({
|
|
mixins: [CommonProps, mainMixin, FHEMixin],
|
|
setup () {
|
|
const theme = useTheme();
|
|
return {
|
|
theme,
|
|
toggleTheme: () => {
|
|
return theme.global.name.value = theme.global.name.value === 'nonEncryptedTheme' ? 'encryptedTheme' : 'nonEncryptedTheme';
|
|
}
|
|
}
|
|
},
|
|
created() {
|
|
|
|
},
|
|
data() {
|
|
return {
|
|
qrInfo: {
|
|
expanded: false,
|
|
size: "50px",
|
|
radius: "5px",
|
|
padding: "5px",
|
|
componentSize: 50
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
gotIt() {
|
|
window.localStorage.setItem('alreadySawTip', 1);
|
|
this.showEncryptionInfo = false;
|
|
},
|
|
toggleQR() {
|
|
if (this.qrInfo.expanded) {
|
|
this.qrInfo = {
|
|
expanded: false,
|
|
size: "50px",
|
|
radius: "5px",
|
|
padding: "5px",
|
|
componentSize: 50
|
|
};
|
|
} else {
|
|
this.qrInfo = {
|
|
expanded: true,
|
|
size: "300px",
|
|
radius: "20px",
|
|
padding: "20px",
|
|
componentSize: 300
|
|
};
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
infoBoxAnimatedStyle() {
|
|
let bgColor = "rgba(10, 10, 10, 0.4)";
|
|
let infoHeight = "35px";
|
|
|
|
if (this.info !== "") {
|
|
infoHeight = "35px";
|
|
if (this.info.indexOf("Error:") !== -1) {
|
|
bgColor = "rgba(200, 100, 100, 0.6)";
|
|
}
|
|
}
|
|
return { "--info-height" : infoHeight, "--bg-color": bgColor };
|
|
},
|
|
qrStyle() {
|
|
return { "--qr-size": this.qrInfo.size, "--qr-radius": this.qrInfo.radius, "--qr-padding": this.qrInfo.padding };
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="main">
|
|
<div class="settings-panel">
|
|
<div id="logo" style="position: absolute; padding: 10px; left:3px; height: 100px; width: 320px">
|
|
<img class="logo" src="~/assets/new_logo.png" />
|
|
</div>
|
|
|
|
<div style="height: 120px;"></div> <!-- spacer -->
|
|
|
|
<v-btn class="btn connect-wallet" :class="isConnected ? 'connected-wallet' : 'connect-wallet-animate'" color="#FC4A1A" rounded @click="connect">
|
|
<template v-slot:prepend>
|
|
<div style="height: 24px; width: 24px"><img src="~/assets/metamask.logo.svg" /></div>
|
|
</template>
|
|
{{ isConnected ? "Connected" : "Connect Wallet" }}
|
|
</v-btn>
|
|
|
|
<div style="height: 30px;"></div> <!-- spacer -->
|
|
|
|
<div v-if="isConnected" class="account-info">
|
|
<div>Account: <span style="font-family: monospace">{{ shortAddress(account) }}</span></div>
|
|
<div style="margin-bottom: 0px;">Balance: <span style="font-family: monospace; font-weight: bold">{{ walletBalanceChecking ? "refreshing..." : walletBalance.toFixed(3) }}</span> <span v-if="!walletBalanceChecking" style="font-family: monospace">tFHE</span></div>
|
|
<v-btn class="btn" :class="showLowTokenWarning && !(usingFaucet || walletBalanceChecking) ? 'button-focus-animation' : ''" :loading="usingFaucet || walletBalanceChecking" color="primary" rounded @click="requestCoinsFromFaucet()" :disabled="account === '' || usingFaucet || walletBalanceChecking || walletBalance > 0.5" style="margin-top: 10px">
|
|
<template v-slot:default>
|
|
<!-- <v-tooltip activator="parent" location="bottom">Get coins from the faucet if you to allow execute transactions</v-tooltip> -->
|
|
Get coins
|
|
</template>
|
|
</v-btn>
|
|
<div v-if="showLowTokenWarning" style="font-size: 11px">You need more coins to make transactions</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
<div class="content-panel">
|
|
<div v-if="!isConnected" class="content-blocker">
|
|
</div>
|
|
<div>
|
|
<div class="action-panel" :class="enableEncryption ? 'encrypted' : 'non-encrypted'">
|
|
|
|
<div v-if="showEncryptionInfo" style="position: absolute; top: 55px; right: -50px; z-index: 20">
|
|
<div class="triangle-tooltip">
|
|
<div>
|
|
<table>
|
|
<tr>
|
|
<td style="white-space: nowrap; vertical-align:top"><span style="color: red">Off</span> - </td>
|
|
<td>Standard ERC20 contract</td>
|
|
</tr>
|
|
<tr><td colspan="2" style="font-size: 2px"> </td></tr>
|
|
<tr>
|
|
<td style="white-space: nowrap; vertical-align:top"><span style="color: green">On</span> - </td>
|
|
<td>Modified ERC20 contract with FHE encryption enabled</td>
|
|
</tr>
|
|
<tr><td colspan="2" style="padding-top: 10px; text-align: right">
|
|
<v-btn density="compact" rounded color="primary" @click="gotIt">Got it!</v-btn>
|
|
</td></tr>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
<span class="triangle"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="font-size: 24px; display: flex; align-items: center; gap: 10px">
|
|
<b>Contract:</b> <span style="font-family: monospace">{{ shortAddress(contractAddress) }}</span>
|
|
<v-btn color="primary" density="compact" icon @click="copyToClipboard(contractAddress)" size="small">
|
|
<template v-slot:default>
|
|
<v-tooltip activator="parent" location="bottom">Copy</v-tooltip>
|
|
|
|
<v-icon size="x-small" icon="mdi-content-copy"></v-icon>
|
|
</template>
|
|
</v-btn>
|
|
|
|
<div style="flex-grow: 1"></div>
|
|
|
|
<div style="font-size: 10px">
|
|
<v-switch
|
|
v-model="enableEncryption"
|
|
hide-details
|
|
color="green"
|
|
density="compact"
|
|
|
|
>
|
|
<template v-slot:label>
|
|
<div style="display: flex; gap: 4px"><div>Encryption:</div> <div style="font-weight: bold; width: 25px" :color="enableEncryption ? 'green' : 'red'">{{enableEncryption ? 'On' : 'Off'}}</div></div>
|
|
</template>
|
|
</v-switch>
|
|
</div>
|
|
|
|
</div>
|
|
<div style="width: 100%; height: 1px; background-color: rgba(255, 255, 255, 0.171);"></div>
|
|
|
|
<div style="margin-top: 10px; width: 100%; flex-grow: 1; position: relative; overflow: hidden;">
|
|
<div>Contract Balance: {{ balance }} {{ enableEncryption ? 'FHET' : 'TKN' }}</div>
|
|
<transition name="fade">
|
|
<div class="lock-container" v-if="enableEncryption && showEncryptionAnimation">
|
|
<audio ref="audioPlayer" :src="audioSource" />
|
|
<Vue3Lottie
|
|
ref="lottieEncryptionOnAnimation"
|
|
:animationData="encryptionOn"
|
|
:loop="false"
|
|
@onComplete="encryptionAnimationComplete"
|
|
/>
|
|
</div>
|
|
</transition>
|
|
<v-carousel v-model="pageIdx" style="height: 100%" hide-delimiters :show-arrows="false">
|
|
<v-carousel-item>
|
|
<div class="page">
|
|
<div style="line-height: 30px">
|
|
<template v-if="parseFloat(balance) < 0.1">
|
|
Oops... it looks like you don't have tokens in the contract.<br/>
|
|
You should mint some tokens or request someone to send you some.
|
|
</template>
|
|
<template v-else>
|
|
Mint some tokens to continue
|
|
</template>
|
|
|
|
</div>
|
|
<div class="empty-wallet-image"><img src="~/assets/empty-wallet.webp" style="width: 150px"/></div>
|
|
<v-btn :disabled="showLowTokenWarning || minting" :loading="minting" color="primary" rounded style="position: absolute; bottom: 10px; right: 10px" @click="mintToken(10)">Mint 10 Tokens</v-btn>
|
|
</div>
|
|
|
|
</v-carousel-item>
|
|
<v-carousel-item>
|
|
<div class="page">
|
|
<div style="line-height: 30px">
|
|
<div style="font-weight: bold; font-size: 16px">Send Tokens:</div>
|
|
<div style="display: flex; gap: 10px">
|
|
<v-text-field :rules="recipientRules" ref="recipient" style="flex-grow: 2;" density="compact" label="Send To"></v-text-field>
|
|
<v-text-field :rules="amountRules" ref="amount" variant="filled" style="flex-grow: 1;" density="compact" label="Amount"></v-text-field>
|
|
<v-btn :disabled="transferring || minting" :loading="transferring" color="primary" rounded style="" @click="sendTokens()">
|
|
Send
|
|
</v-btn>
|
|
</div>
|
|
|
|
<div style="font-weight: bold; font-size: 16px">{{ enableEncryption ? "Unwrap" : "Wrap" }} Tokens: <span style="font-size: 10px; font-weight: normal">(convert to {{ enableEncryption ? "non-encrypted" : "encrypted" }} token)</span></div>
|
|
<div style="display: flex; gap: 10px">
|
|
<v-text-field :rules="amountRules" ref="wrapAmount" variant="filled" style="flex-grow: 1;" density="compact" label="Amount"></v-text-field>
|
|
<v-btn :disabled="transferring || minting || balance <= 0" :loading="transferring" color="primary" rounded style="" @click="wrapTokens()">
|
|
{{ enableEncryption ? "Unwrap" : "Wrap" }}
|
|
</v-btn>
|
|
</div>
|
|
|
|
</div>
|
|
<v-btn :disabled="showLowTokenWarning || minting || transferring" :loading="minting" color="primary" rounded style="position: absolute; bottom: 10px; left: 10px" @click="mintToken(10)">Mint 10 More Tokens</v-btn>
|
|
</div>
|
|
|
|
</v-carousel-item>
|
|
</v-carousel>
|
|
</div>
|
|
</div>
|
|
<v-expand-transition>
|
|
<div class="info-box" v-show="info !== ''" :style="infoBoxAnimatedStyle" >
|
|
<div>
|
|
{{ info }}
|
|
</div>
|
|
<div v-if="showProgress" style="position: absolute; left: 15px; bottom: 0px; width: calc(100% - 30px); height: 3px">
|
|
<v-progress-linear style="width: 100%; height: 3px"
|
|
indeterminate
|
|
color="orange-darken-2"
|
|
></v-progress-linear>
|
|
</div>
|
|
</div>
|
|
</v-expand-transition>
|
|
</div>
|
|
<div @click="toggleQR" class="qr-container" :style="qrStyle">
|
|
<qrcode-vue style="width: 100%; height: 100%" :size="500" :value="mmDeepLink" level="H" />
|
|
</div>
|
|
|
|
|
|
</div>
|
|
<div class="history-panel">
|
|
<v-list style="width: 95%; background: none; color: white">
|
|
<v-list-subheader color="white">Last UI Activity</v-list-subheader>
|
|
|
|
<v-list-item
|
|
v-for="(item, i) in historyItems"
|
|
:key="i"
|
|
:value="item"
|
|
color="primary"
|
|
>
|
|
<template v-slot:prepend>
|
|
<v-tooltip activator="parent" location="start">{{ item.encrypted ? "Encrypted" : "Not Encrypted"}}</v-tooltip>
|
|
<v-icon :icon="item.encrypted ? 'mdi-lock' : 'mdi-lock-open'"></v-icon>
|
|
</template>
|
|
<template v-slot:append>
|
|
<v-btn icon="mdi-open-in-new" density="compact" @click="openExplorer(item.tx)" ></v-btn>
|
|
</template>
|
|
<v-list-item-title>{{ shortAddress(item.tx) }}</v-list-item-title>
|
|
<v-list-item-subtitle>Action: {{ item.action }}</v-list-item-subtitle>
|
|
<v-list-item-subtitle>Status: {{ item.status }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</v-list>
|
|
<div style="width: 100%; text-align: center; margin-top: 20px">
|
|
<v-btn color="primary" rounded v-if="historyItems.length > 0" @click="clearHistory">Clear History</v-btn>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<style scoped>
|
|
|
|
.main {
|
|
width: 100vw;
|
|
color: white;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.settings-panel {
|
|
min-width: 280px;
|
|
height: 100vh;
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
background-image: linear-gradient(
|
|
to bottom,
|
|
rgba(0, 0, 0, 0.0),
|
|
rgba(10, 10, 10, 0.3)
|
|
);
|
|
backdrop-filter: blur(7px);
|
|
border-right-width: 2px;
|
|
border-image:
|
|
linear-gradient(
|
|
to bottom,
|
|
rgba(0, 0, 0, 0.0) 100px,
|
|
#FF5C00
|
|
) 0 100%;
|
|
}
|
|
|
|
.history-panel {
|
|
min-width: 380px;
|
|
height: 100vh;
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
|
|
background-image: linear-gradient(
|
|
to bottom,
|
|
rgba(0, 0, 0, 0.0),
|
|
rgba(10, 10, 10, 0.3)
|
|
);
|
|
backdrop-filter: blur(7px);
|
|
border-left-width: 2px;
|
|
border-image:
|
|
linear-gradient(
|
|
to bottom,
|
|
rgba(0, 0, 0, 0.0) 100px,
|
|
#FF5C00
|
|
) 0 100%;
|
|
|
|
}
|
|
|
|
|
|
.content-blocker {
|
|
position: absolute;
|
|
top: 90px;
|
|
width: 100%;
|
|
height: 100%;
|
|
/* background-color: rgba(0, 0, 0, 0.5); */
|
|
background: linear-gradient(0deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.1) 80%, rgba(0,0,0,0.0) 100%);
|
|
|
|
|
|
z-index: 99;
|
|
backdrop-filter: blur(7px);
|
|
}
|
|
|
|
.content-panel {
|
|
position: relative;
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100vh;
|
|
}
|
|
|
|
.logo {
|
|
height: 70px;
|
|
margin-bottom: 20px;
|
|
z-index: 100;
|
|
}
|
|
|
|
|
|
.btn {
|
|
width: auto;
|
|
color: white
|
|
}
|
|
|
|
.connect-wallet {
|
|
max-width: 200px;
|
|
}
|
|
|
|
|
|
|
|
.connect-wallet-animate::after {
|
|
content: '';
|
|
width: 30px; height: 30px;
|
|
border-radius: 100%;
|
|
border: 6px solid #ff6600;
|
|
position: absolute;
|
|
z-index: -1;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
animation: ring-1 1.5s infinite;
|
|
}
|
|
|
|
.button-focus-animation::after {
|
|
content: '';
|
|
width: 30px; height: 30px;
|
|
border-radius: 100%;
|
|
border: 3px solid #ff6600;
|
|
position: absolute;
|
|
z-index: -1;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
animation: ring-2 1.5s infinite;
|
|
}
|
|
|
|
@keyframes ring-1 {
|
|
0% {
|
|
width: 30px;
|
|
height: 30px;
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
width: 200px;
|
|
height: 200px;
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes ring-2 {
|
|
0% {
|
|
width: 30px;
|
|
height: 30px;
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
width: 100px;
|
|
height: 100px;
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.connected-wallet {
|
|
margin-left: -30px;
|
|
}
|
|
|
|
.action-panel {
|
|
margin-left: 20px;
|
|
margin-top: 100px;
|
|
padding: 10px;
|
|
width: 600px;
|
|
height: 370px;
|
|
border-radius: 15px;
|
|
|
|
backdrop-filter: blur(7px);
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.274);
|
|
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: stretch;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.action-panel.non-encrypted {
|
|
background-color: rgba(0, 37, 61, 0.13);
|
|
}
|
|
|
|
.action-panel.encrypted {
|
|
background-color: rgba(61, 24, 0, 0.13);
|
|
}
|
|
|
|
.account-info {
|
|
|
|
border-radius: 15px;
|
|
padding: 10px;
|
|
width: 95%;
|
|
|
|
/* border: 2px solid white; */
|
|
border-left-width: 1px;
|
|
border-right-width: 1px;
|
|
border-image:
|
|
linear-gradient(
|
|
to top,
|
|
rgba(0, 0, 0, 0.0) ,
|
|
rgba(255, 255, 255, 0.5),
|
|
rgba(0, 0, 0, 0.0)
|
|
) 0 100%;
|
|
}
|
|
|
|
.empty-wallet-image {
|
|
position: absolute;
|
|
left: 10px;
|
|
bottom: 10px;
|
|
z-index: -1;
|
|
-webkit-mask-image: -webkit-gradient(linear, right bottom, left top, from(rgba(0,0,0,0.2)), to(rgba(0,0,0,0)));
|
|
}
|
|
|
|
.contract-box {
|
|
background-color: white;
|
|
color: black;
|
|
padding: 6px;
|
|
border-radius: 8px;
|
|
z-index: 2;
|
|
box-shadow: inset 0 0 1px black;
|
|
}
|
|
|
|
.info-box {
|
|
position: relative;
|
|
margin-left: 20px;
|
|
width: 600px;
|
|
height: var(--info-height);
|
|
margin-top: -7px;
|
|
border-radius: 0px 0px 15px 15px;
|
|
background-color: var(--bg-color);
|
|
backdrop-filter: blur(7px);
|
|
z-index: -1;
|
|
/* line-height: 45px; */
|
|
padding-top: 12px;
|
|
padding-left: 10px;
|
|
font-size: 12px;
|
|
transition-property: height;
|
|
transition-timing-function: cubic-bezier(0.47, 1.64, 0.41, 0.8);
|
|
transition-duration: 0.3s;
|
|
}
|
|
|
|
.lock-container {
|
|
position: absolute;
|
|
z-index: 90;
|
|
width: 200px;
|
|
height: 200px;
|
|
background-color: rgba(0, 0, 0, 0.8);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
backdrop-filter: blur(7px);
|
|
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.pages {
|
|
display: flex;
|
|
width: 200%;
|
|
height: 100%;
|
|
/* box-sizing: border-box; */
|
|
}
|
|
|
|
.page {
|
|
width: 100%;
|
|
height: calc(100% - 20px);
|
|
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
padding-top: 10px;
|
|
transition: all 0.7s;
|
|
}
|
|
|
|
.data-table {
|
|
background-color: rgba(0, 0, 0, 0.075);
|
|
border-collapse: separate;
|
|
border-spacing: 2px; /* Adjust as needed */
|
|
backdrop-filter: blur(7px);
|
|
min-width: 450px;
|
|
}
|
|
|
|
.data-table th {
|
|
padding: 5px;
|
|
background-color: #FC4A1A;
|
|
text-align: left;
|
|
}
|
|
|
|
.data-table td {
|
|
padding: 5px;
|
|
background-color: rgba(128, 128, 128, 0.164);
|
|
}
|
|
|
|
</style>
|
|
|
|
<style lang="scss" scoped>
|
|
.triangle-tooltip {
|
|
position: relative;
|
|
background-color: rgba(0, 0, 0, 1);
|
|
padding: 10px;
|
|
width: 350px;
|
|
height: 130px;
|
|
border-radius: 0.55em;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
border: 2px solid rgba(255, 255, 255, 0.164);
|
|
|
|
}
|
|
|
|
.triangle {
|
|
display: block;
|
|
height: 20px;
|
|
width: 20px;
|
|
background-color: inherit;
|
|
border: inherit;
|
|
position: absolute;
|
|
top: -10px;
|
|
left: calc(50% - 10px);
|
|
clip-path: polygon(0% 0%, 100% 100%, 0% 100%);
|
|
transform: rotate(135deg);
|
|
border-radius: 0 0 0 0.25em;
|
|
}
|
|
|
|
.qr-container {
|
|
position: absolute;
|
|
bottom: 30px;
|
|
left: 30px;
|
|
height: var(--qr-size);
|
|
width: var(--qr-size);
|
|
background-color: white;
|
|
padding: var(--qr-padding);
|
|
border-radius: var(--qr-radius);
|
|
cursor: pointer;
|
|
transition: all .4s cubic-bezier(.47,1.64,.41,.8);
|
|
}
|
|
|
|
|
|
|
|
</style>
|