This commit is contained in:
yuyongdong 2024-06-06 17:58:33 +08:00
commit 96b6451e2d
84 changed files with 26988 additions and 0 deletions

7
.env.development Normal file
View File

@ -0,0 +1,7 @@
VITE_BASE_API='https://oauth-svr.cebggame.com/mint'
VUE_APP_GPAL_API='http://192.168.100.83:4000/sns'
VITE_TOKENID_ID = '0x34a1'
VITE_ERC_ADDRESS = '0xFd42bfb03212dA7e1A4608a44d7658641D99CF34'
VITE_CFNFT_ADDRESS = '0xaa34B79A0Ab433eaC900fB3CB9f191F5Cd27501D'
VITE_CLAIM_ADDRESS = '0xf45702180314187a3549FEDac3B78349b47ca6A0'
VITE_CLAIMWL_ADDRESS = '0x31f29c9a3d0c1c13c825475aebf0d964b5b47c45'

7
.env.production Normal file
View File

@ -0,0 +1,7 @@
VITE_BASE_API='https://oauth-svr.cebggame.com/mint'
VUE_APP_GPAL_API='http://192.168.100.83:4000/sns'
VITE_TOKENID_ID = '0x34a1'
VITE_ERC_ADDRESS = '0xFd42bfb03212dA7e1A4608a44d7658641D99CF34'
VITE_CFNFT_ADDRESS = '0xaa34B79A0Ab433eaC900fB3CB9f191F5Cd27501D'
VITE_CLAIM_ADDRESS = '0xf45702180314187a3549FEDac3B78349b47ca6A0'
VITE_CLAIMWL_ADDRESS = '0x31f29c9a3d0c1c13c825475aebf0d964b5b47c45'

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously Volar) and disable Vetur

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

17360
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "mint",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host --mode dev",
"build": "vite build --mode dev",
"build:test": "vite build --mode test",
"build:prod": "vite build --mode production",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"buffer": "^6.0.3",
"element-plus": "^2.7.3",
"ethers": "5.4",
"postcss-px-to-viewport": "^1.1.1",
"sass": "^1.77.2",
"vue": "^3.4.21",
"rollup-plugin-polyfill-node": "^0.12.0",
"web3": "^4.9.0"
},
"devDependencies": {
"@imtbl/sdk": "^1.36.4",
"@vitejs/plugin-vue": "^5.0.4",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"vite": "^5.2.0",
"vite-plugin-node-polyfills": "^0.22.0"
}
}

24
postcss.config.cjs Normal file
View File

@ -0,0 +1,24 @@
module.exports = {
plugins: {
'postcss-px-to-viewport': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 1920, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: [], // 需要忽略的CSS选择器不会转为视口单位使用原有的px等单位。
minPixelValue: 1, // 设置最小的转换数值如果为1的话只有大于1的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
include: undefined, // 如果设置了include那将只有匹配到的文件才会被转换
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1920 // 横屏时使用的视口宽度
}
}
}

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

116
src/App.vue Normal file
View File

@ -0,0 +1,116 @@
<script setup>
import HelloWorld from './view/mintIndex.vue'
import FooterView from './components/footerView.vue'
</script>
<template>
<!-- <div>
<div @click="immuTableLogin">immuTableLogin</div>
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div> -->
<HelloWorld />
<FooterView />
</template>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #fff;
}
html,
body,
div,
span,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
abbr,
address,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
samp,
small,
strong,
sub,
sup,
var,
b,
i,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
list-style: none;
text-align: center;
}
</style>

1339
src/abi/CFNFTGame.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

492
src/abi/NFTClaimStage2.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

63
src/abi/mint.js Normal file
View File

@ -0,0 +1,63 @@
const MINT_CONTRACT_ADDRESS='0x31f29c9a3d0c1c13c825475aebf0d964b5b47c45'
const RPC='https://rpc.testnet.immutable.com/'
const requestChain = async (rpc, method, params) => {
const data = {
id: Date.now(),
jsonrpc: '2.0',
method,
params,
}
const options: any = {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify(data),
}
return fetch(rpc, options, DEFAULT_TIMEOUT).then(res => res.json())
}
const queryMintData = async(user, data) => {
const params = [{
from: user,
to: MINT_CONTRACT_ADDRESS,
data,
}, 'latest']
return requestChain(RPC, 'eth_call', params)
}
// 当前用户白单数量
const fetchWLCount = async(user) => {
const data = '0xe65ee803'
return queryMintData(user, data)
}
/**
* mintConfig
* {
* maxSupply: 2000, // 当前活动可mint总数
* currency: 0x21321, // 需要支付的代币地址
* mintPrice: 100000000000000, // 需要支付的代币数量, 单位: wei
* feeToAddress: 0x1231231 // 接收代币的钱包地址, 前端不需要管
* }
*/
const fetchMintConfig = async() => {
const data = '0xe7cc7244'
return queryMintData(MINT_CONTRACT_ADDRESS, data)
}
// 所有用户已mint的nft总数
const fetchMintedCount = async()=> {
const data = '0x34eafb11'
return queryMintData(MINT_CONTRACT_ADDRESS, data)
}
// 已mint的NFT列表
const fetchUserNftList = async(user) => {
const data = '0x7d10ab3f'
return queryMintData(user, data)
}

BIN
src/assets/home/Arrow2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

BIN
src/assets/home/Arrow3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
src/assets/home/Discord.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

BIN
src/assets/home/ETHicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

BIN
src/assets/home/Icon_!.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

BIN
src/assets/home/Opening.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

BIN
src/assets/home/X.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/home/andriod.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
src/assets/home/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/home/arrow1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
src/assets/home/blur.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/home/google.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
src/assets/home/loding .png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/assets/home/mint bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/assets/home/nav bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/assets/home/nav.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
src/assets/home/okx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
src/assets/home/playBtn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
src/assets/text/text.css Normal file
View File

@ -0,0 +1,18 @@
@font-face {
font-family: 'Anton';
font-style: normal;
font-weight: 600;
src: url('./Anton-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
src: url('./Poppins-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Poppins-SemiBold';
font-style: normal;
font-weight: 600;
src: url('./Poppins-SemiBold.ttf') format('truetype');
}

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,52 @@
<template>
<!-- <div @click="immuTableLogin">immuTableLogin</div> -->
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<script setup>
import { ref } from 'vue'
import { getConnect } from "./../wallet/index.js"
// defineProps({
// msg: String,
// })
const count = ref(0)
// const immuTableLogin = async () => {
// console.log('----')
// try{
// const walletLogin = await getConnect()
// console.log(walletLogin)
// } catch (e) {
// console.log(e);
// }
// }
</script>
<style scoped>
.read-the-docs {
color: #888;
}
</style>../wallet/index1.js

View File

@ -0,0 +1,57 @@
<template>
<div class="footer">
<div class="footer-left">COPYRIGHT © Candy Bubble Global Limited.ALL RIGHTS RESERVED.</div>
<div class="footer-right">
<li>
<a href="https://discord.com/invite/counterfire" target="_blank">
<img src="./../assets/home/com_Discord_icon.png" alt="">
</a>
</li>
<li>
<a href="https://twitter.com/@playCounterFire" target="_blank">
<img src="./../assets/home/com_X_icon.png" alt="">
</a>
</li>
<li>
<a href="https://youtube.com/@_CounterFire" target="_blank">
<img src="./../assets/home/com_Youtube_icon.png" alt="">
</a>
</li>
</div>
</div>
</template>
<script>
</script>
<style lang="scss" scoped>
.footer {
width: 100%;
height: 60px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 45px;
box-sizing: border-box;
border-top: 2px solid #1A1821;
background: #0f1013;
.footer-left {
color: #877E98;
font-size: 18px;
}
.footer-right {
display: flex;
li {
width: 31px;
height: 24px;
margin-left: 20px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
}
</style>

View File

@ -0,0 +1,86 @@
<template>
<el-dialog :model-value="Loading" :show-close="false" :modal="false" :close-on-click-modal="false" class="RecentlyLoading">
<div class="recentlyTit">
<div>
<img src="./../assets/home/loding .png" alt />
</div>
<p>Please wait. Data is loading</p>
</div>
</el-dialog>
</template>
<script>
import { ElDialog } from "element-plus";
export default {
name: "Loading",
props: {
Loading: Boolean
},
data() {
return {
// Loading:
};
}
};
</script>
<style lang="scss" scoped>
.el-overlay-dialog{
// .RecentlyLoading {
// ::v-deep
.RecentlyLoading .el-dialog {
width: 480px;
// height: 60px;
background: url("@/assets/common/bg button.png") no-repeat;
background-size: 100% 100%;
margin-top: 85vh !important;
.el-dialog__header {
padding: 0;
height: 0;
}
.el-dialog__body {
padding: 0;
}
.recentlyTit {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
div {
width: 30px;
height: 30px;
img {
width: 100%;
height: 100%;
animation: turn 3s linear infinite;
}
}
p {
color: #fff;
font-size: 16px;
text-align: center;
margin-left: 20px;
}
}
}
// }
}
@keyframes turn {
0% {
-webkit-transform: rotate(0deg);
}
25% {
-webkit-transform: rotate(90deg);
}
50% {
-webkit-transform: rotate(180deg);
}
75% {
-webkit-transform: rotate(270deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div>
<div class="count-down">
{{ day }}&nbsp;
<span>D :&nbsp;</span>
{{ hour }}&nbsp;
<span>H :&nbsp;</span>
{{ min }}&nbsp;
<span>M :&nbsp;</span>
{{ sec }}&nbsp;
<span>S</span>
</div>
</div>
</template>
<script>
export default {
props: {
//
//
startTime: {
type: Number,
require: true
},
endTime: {
type: Number,
require: true
}
},
setup(props) {
//
const day = ref('--')
//
const hour = ref('--')
//
const min = ref('--')
//
const sec = ref('--')
//
const start = ref(false)
onMounted(() => {
//
getTimes();
});
onBeforeUnmount(() => {
//
window.clearInterval(timer);
});
const getTimes = () => {
const date = new Date().getTime();
//1.
if (Number(date) < Number(props.startTime)) {
//
countDown(props.startTime);
}
//2.
//
if (
Number(date) >= Number(props.startTime) &&
Number(date) < Number(props.endTime)
) {
//
countDown(props.endTime);
}
//3.
if (Number(date) > Number(props.endTime)) {
start.value = false;
}
};
const countDown = time => {
//
start.value = true;
let nowTime = new Date().getTime();
let inputTime = time;
let timer = null;
//- == 0
let times = (inputTime - nowTime) / 1000;
timer = setInterval(() => {
if (times == 0) {
// getTimes()
// getTimes()
// getTimes() 3
getTimes();
clearInterval(timer);
window.clearInterval(timer);
timer = null;
return;
}
// times1
times--;
//js
let d = parseInt(String(times / 60 / 60 / 24));
d = d < 10 ? "0" + d : d;
//js
let h = parseInt(String((times / 60 / 60) % 24));
h = h < 10 ? "0" + h : h;
//js
let m = parseInt(String((times / 60) % 60));
m = m < 10 ? "0" + m : m;
//js
let s = parseInt(String(times % 60));
s = s < 10 ? "0" + s : s;
//
day.value = d;
hour.value = h;
min.value = m;
sec.value = s;
}, 1000);
};
return { day, hour, min, sec, start };
}
};
</script>

View File

@ -0,0 +1,72 @@
<template>
<div>
{{hour}}h:{{min}}m:{{sec}}s
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const props = defineProps({
getAddress: {
type: String,
required: true,
},
});
let remainingTime = ref(0); // 24 = 86400
const day = ref(0);
const hour = ref(0);
const min = ref(0);
const sec = ref(0);
let intervalId = null
const starTimer = () => {
let nowTime = new Date().getTime();
// let str = localStorage.getItem(`openTime${props.getAddress}`)
let strTime = (nowTime+86400000 - nowTime) / 1000
intervalId = setInterval(() => {
if (strTime >= 1) {
// remainingTime.value -= 1;
strTime--;
//js
let d = Math.floor(strTime / 60 / 60 / 24);
d = d < 10 ? "0" + d : d;
//js
let h = Math.floor((strTime / 60 / 60) % 24);
h = h < 10 ? "0" + h : h;
//js
let m = Math.floor((strTime / 60) % 60);
m = m < 10 ? "0" + m : m;
//js
let s = Math.floor(strTime % 60);
s = s < 10 ? "0" + s : s;
//
day.value = d;
hour.value = h;
min.value = m;
sec.value = s;
} else {
localStorage.removeItem(`openTime${props.getAddress}`)
clearInterval(intervalId);
intervalId = null
}
}, 1000);
}
// mint
const isOpenTime = () => {
let openTime = localStorage.getItem(`openTime${props.getAddress}`)
// if(props.getAddress) {
// if(props.getAddress == openTime.split('-')[0]) {
starTimer()
// }
// }
}
onMounted(() => {
isOpenTime()
//
onUnmounted(() => {
clearInterval(intervalId);
});
});
</script>

14
src/main.js Normal file
View File

@ -0,0 +1,14 @@
import { createApp } from 'vue'
import './style.css'
import './assets/text/text.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import passportInit from './wallet/initPassport.js'
const app = createApp(App)
app.use(ElementPlus)
app.use(passportInit)
app.mount('#app')

90
src/style.css Normal file
View File

@ -0,0 +1,90 @@
/* :root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
} */
body {
padding: 0 !important;
margin: 0 !important;
}
h1 {
margin: 0;
}
p {
margin: 0;
}

130
src/utils/webapi.js Normal file
View File

@ -0,0 +1,130 @@
// const API_BASE = import.meta.env.VUE_APP_BASE_API;
// console.log(API_BASE)
// import axios from 'axios'
// const toJson = res => res.json();
// const httpPost = async (url, data) => {
// const token = store.state.user.token;
// let headers = {"Content-Type": "application/json"};
// // let token = token;
// if (token) {
// headers['Authorization'] = `Bearer ${token}`;
// }
// return fetch(url, {
// method: "POST",
// body: JSON.stringify(data),
// headers
// }).then(toJson);
// }
// const httpGet = async (url) => {
// const token = ''
// let headers = {"Content-Type": "application/json"};
// if (token) {
// headers['Authorization'] = `Bearer ${token}`;
// }
// return fetch(url, {
// method: "GET",
// headers
// }).then(toJson);
// }
// const base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
// export const hexToBase58 = (hexString) => {
// hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16));
// let base58String = '';
// // eslint-disable-next-line no-undef
// let num = BigInt('0x' + hexString);
// while (num > 0n) {
// const remainder = num % 58n;
// num = num / 58n;
// base58String = base58Alphabet[Number(remainder)] + base58String;
// }
// return base58String;
// }
// export const aesEncrypt = (plaintText, key) => {
// key = CryptoJS.SHA1(key).toString().substring(0,16)
// key = CryptoJS.enc.Base64.parse(key)
// let encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
// mode: CryptoJS.mode.ECB,
// padding: CryptoJS.pad.Pkcs7
// });
// return encryptedData.toString(CryptoJS.format.Hex);
// }
// export const loginNonce = async (address) => {
// const nonceRes = await fetch(`${API_BASE}/api/wallet/nonce?address=${address}`).then(toJson);
// const { nonce } = nonceRes.data;
// return nonce;
// }
// export const loginWithSignature = async (message, signature, activity, wallet) => {
// let fps = await fp.load();
// let result = await fps.get()
// const clientId = result.clientId;
// return fetch(`${API_BASE}/api/wallet/login`, {
// method: "POST",
// body: JSON.stringify({ message, signature, activity,clientId, wallet }),
// headers: {
// "Content-Type": "application/json",
// },
// }).then(toJson);
// }
// export const beginPost = async (activity) => {
// return fetch(`${API_BASE}/api/tasks/begin_task`, {
// method: "POST",
// body: JSON.stringify({ activity }),
// headers: {
// "Content-Type": "application/json",
// },
// }).then(toJson);
// }
// /**
// * past 方法
// * @param {string} url [请求地址]
// * @param {object} params [请求参数]
// */
// function post(url, params) {
// return new Promise((resolve, reject) => {
// axios
// .post(url, QS.stringify(params))
// .then((res) => {
// resolve(res.data);
// })
// .catch((err) => {
// reject(err.data);
// });
// });
// }
// // 查询是否在白名单
// export const apiCheck = async (address) => {
// const url = `${API_BASE}/`;
// return httpGet(url, {address})
// }
// // 获取claim方法的参数
// export const apiSendCode = async (email) => {
// const url = `${API_BASE}/api/email/send_code`;
// return httpPost(url, {email})
// }
// // 活动时间结束
// export const endActivity = async (time) => {
// var now = new Date().getTime();
// if(time <= now) {
// return false
// } else {
// return true
// }
// }

2455
src/view/mintIndex copy.vue Normal file

File diff suppressed because it is too large Load Diff

2455
src/view/mintIndex.vue Normal file

File diff suppressed because it is too large Load Diff

441
src/wallet/index.js Normal file
View File

@ -0,0 +1,441 @@
import Web3 from 'web3'
import { config, passport } from '@imtbl/sdk';
import { ethers, providers } from 'ethers';
import ERC_abi from './../abi/ImmutableERC20MinterBurnerPermit.json'
import CFNFT_abi from './../abi/CFNFTGame.json'
import CLAIM_abi from './../abi/NFTClaimStage2.json'
import CLAIMWL_abi from './../abi/NFTClaimStage2WL.json'
var abis = {
"ERC": ERC_abi,
"CFNFT": CFNFT_abi,
"CLAIM": CLAIM_abi,
"CLAIMWL": CLAIMWL_abi,
}
var ERCAddress = import.meta.env.VITE_ERC_ADDRESS
var CFNFTAddress = import.meta.env.VITE_CFNFT_ADDRESS
var claimAddress = import.meta.env.VITE_CLAIM_ADDRESS
var claimWlAddress = import.meta.env.VITE_CLAIMWL_ADDRESS
var chainId = import.meta.env.VITE_TOKENID_ID
// var connectUrl = process.env.VUE_APP_CONFIG_URL
console.log(CFNFTAddress,claimAddress,chainId)
// 公用方法
const MINT_CONTRACT_ADDRESS='0x31f29c9a3d0c1c13c825475aebf0d964b5b47c45'
const RPC='https://rpc.testnet.immutable.com/'
const requestChain = async (rpc, method, params) => {
const data = {
id: Date.now(),
jsonrpc: '2.0',
method,
params,
}
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
body: JSON.stringify(data),
}
return fetch(rpc, options, DEFAULT_TIMEOUT).then(res => res.json())
}
const queryMintData = async(user, data) => {
const params = [{
from: user,
to: MINT_CONTRACT_ADDRESS,
data,
}, 'latest']
return requestChain(RPC, 'eth_call', params)
}
// const checkoutSDK = new checkout.Checkout()
// const type = checkout.WalletFilterTypes.ALL;
// window.addEventListener('load', function() {
// const passportInstance = new passport.Passport({
// baseConfig: {
// environment: config.Environment.SANDBOX, // or Environment.PRODUCTION
// publishableKey: 'wc:b348768c53d32b69062adcc7f67b263f771564c39edc06acc4cb3e9ee561d415@2?relay-protocol=irn&symKey=b8302aed25de3f9e3485eb8601d27c52779ab28cd8b1398312aa18be01cebbe8', // replace with your publishable API key from Hub
// // publishableKey: '', // replace with your publishable API key from Hub
// },
// clientId: 'eWbQF3aPY83RCNBMYGv2wUAbOJFKZo8l', // replace with your client ID from Hub
// redirectUri: 'http://localhost:5173/', // replace with one of your redirect URIs from Hub
// logoutRedirectUri: 'http://localhost:5173/', // replace with one of your logout URIs from Hub
// audience: '',
// scope: 'openid offline_access email transact',
// popupOverlayOptions: {
// disableGenericPopupOverlay: false, // Set to true to disable the generic pop-up overlay
// disableBlockedPopupOverlay: false, // Set to true to disable the blocked pop-up overlay
// }
// });
// passportInstance.loginCallback();
// });
const passportInstance = new passport.Passport({
baseConfig: {
environment: config.Environment.SANDBOX, // or Environment.PRODUCTION
publishableKey: 'pk_imapik-test-1E-detBXtG7U$5961WuL', // replace with your publishable API key from Hub
// publishableKey: '', // replace with your publishable API key from Hub
},
clientId: 'gYQrjtct7CN4UVwDG2sb8yACfYRo3xQJ', // replace with your client ID from Hub
redirectUri: 'http://localhost:5173/redirect', // replace with one of your redirect URIs from Hub
logoutRedirectUri: 'http://localhost:5173/logout', // replace with one of your logout URIs from Hub
audience: 'platform_api',
scope: 'openid offline_access email transact',
popupOverlayOptions: {
disableGenericPopupOverlay: true, // Set to true to disable the generic pop-up overlay
disableBlockedPopupOverlay: true, // Set to true to disable the blocked pop-up overlay
}
});
// 链接Wallet Connect钱包
export async function getConnect() {
const passportProvider = proxy.$passportInit;
const accounts = await passportProvider.request({ method: "eth_requestAccounts" })
console.log( accounts,'-----------------------------------------------------------------')
return
}
export async function getLogout() {
const profile = await passportInstance.logout();
return
}
export async function linkWallet(val) {
let res
if(val == 'ethereum') {
const web3js = window.ethereum
if (typeof web3js !== "undefined") {
res = await ethereum.request({ method: "eth_requestAccounts" })
}
} else if(val == 'okxwallet') {
res = await okxwallet.request({ method: "eth_requestAccounts" });
}
return res[0]
}
export async function disconnectLink(name) {
if(name == 'ethereum') {
await window.ethereum.request({
"method": "wallet_revokePermissions",
"params": [
{
"eth_accounts": {}
}
]
});
} else if(name == 'okxwallet') {
await okxwallet.request({
"method": "wallet_disconnect",
})
} else if(name == 'connect') {
await passportInstance.logout()
}
}
// Wallet Connect初始化
// import { EthereumProvider } from '@walletconnect/ethereum-provider'
// const provider = await EthereumProvider.init({
// projectId: 'e7743d46923911fa8850619b7a7f6d9d', // required
// chains: ['1'], // required
// showQrModal: true, // requires @walletconnect/modal
// optionalChains: ['1'],
// // optionalMethods: ['eth_signTypedData', 'eth_signTypedData_v4'],
// metadata: {
// name: 'Counter Fire',
// description: 'Counter Fire',
// url: 'https://mint.counterfire.games/',
// icons: ['https://www.counterfire.games/favicon.ico'],
// },
// });
// 链接Wallet Connect钱包
// export async function getConnect() {
// await provider.connect()
// let result = await provider.request({ method: 'eth_requestAccounts' })
// return result
// }
// 获取钱包地址
export async function getWalletAddress() {
const web3js = window.ethereum
const web3 = await web3js.request({
method: 'eth_accounts'
})
if (web3 !== 'undefined') {
if (web3[0] == '') {
return false
} else {
return await web3[0]
}
}
}
// 查看钱包是否链接
export async function isWalletConnected() {
const ethereum = window.web3;
if (typeof ethereum !== 'undefined') {
let res = await ethereum.request({ method: "eth_accounts" })
// await okxwallet.request({ method: "eth_accounts" });
// console.log(res,'279-----')
if(res[0] == '') {
return false
} else {
return true
}
}
return false
}
// 检查是否有arbitrum-sepolia网络
export async function isSepoliNetwork(name,web3) {
let res
if(name == 'ethereum') {
res = await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [
{
chainId: chainId
}
]
});
return false
} else if(name == 'okxwallet') {
res =
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [
{
chainId: chainId
}
]
});
return false
} else if(name == 'connect') {
return false
}
}
// 添加arbitrum-sepolia网络
export async function addNetwork(name) {
if(name == 'ethereum') {
try{
let res = await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: chainId,
chainName: 'Sepolia Testnet Explorer',
rpcUrls: ['https://rpc.testnet.immutable.com'],
iconUrls: ['https://xdaichain.com/fake/example/url/xdai.png'],
blockExplorerUrls: ['https://rpc.testnet.immutable.com'],
nativeCurrency: {
name: "TIMX",
symbol: "TIMX",
decimals: 18
},
},]
})
return await res
} catch(e) {
return false
}
} else if(name == 'okxwallet') {
try{
let res = await okxwallet.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: chainId,
chainName: 'Sepolia Testnet Explorer',
rpcUrls: ['https://rpc.testnet.immutable.com'],
iconUrls: ['https://xdaichain.com/fake/example/url/xdai.png'],
blockExplorerUrls: ['https://rpc.testnet.immutable.com'],
nativeCurrency: {
name: "TIMX",
symbol: "TIMX",
decimals: 18
},
}]
})
return await res
} catch(e) {
return false
}
} else if(name == 'connect') {
try{
let res = await window.provider.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0xaa36a7',
chainName: 'Sepolia Testnet Explorer',
rpcUrls: ['https://sepolia.infura.io/v3'],
iconUrls: ['https://xdaichain.com/fake/example/url/xdai.png'],
blockExplorerUrls: ['https://sepolia.etherscan.io'],
nativeCurrency: {
name: "TIMX",
symbol: "TIMX",
decimals: 18
},
}]
})
return await res
} catch(e) {
return false
}
}
}
const GAS_BOOST = 2
// claim NFT
export const claimStage2Nft = async(name, myAddress, params, web3) => {
let price = params*1e18
if(name == 'connect') {
let gasPrice = await web3.request({ method: 'eth_gasPrice' })
let web3Provider = new providers.Web3Provider(web3)
const signer = web3Provider.getSigner();
const [ userAddress ] = await web3.request({ method: "eth_requestAccounts"});
const contractFT = new ethers.Contract(ERCAddress, abis['ERC'].abi, signer);
// let gasApprove = await contractFT.estimateGas.approve(claimWlAddress, window.BigInt(price))
let gasApprove = await contractFT.estimateGas.approve(claimWlAddress, price)
gasApprove = (parseInt(gasApprove) * GAS_BOOST) | 0;
// await contractFT.approve(claimWlAddress, window.BigInt(price))
await contractFT.approve(claimWlAddress, price)
//------
const contract = new ethers.Contract(claimWlAddress, abis['CLAIMWL'].abi, signer);
// console.log(params, '--------------',contract);
// return
let gas = await contract.estimateGas.claim(params)
// console.log(gas)
// return
return await contract.claim(params)
let res = await web3.request({ method: 'eth_gasPrice' })
console.log(params[1], res)
let abc = await web3.request({ method: 'eth_requestAccounts'})
// let contractFT = await web3.request({ method: 'eth_call',
// params: [
// {
// to: myAddress,
// data: claimAddress
// },
// 'latest'
// ]
// })
} else {
// const account = await web3.eth.getAccounts()
let res = await isSepoliNetwork(name,web3)
if(!res) {
let addRes = await addNetwork(name,web3)
}
const gasPrice = await web3.eth.getGasPrice()
const contractFT = new web3.eth.Contract(abis['ERC'].abi, ERCAddress, {
from: myAddress
});
const allowance = await contractFT.methods.allowance(myAddress, claimWlAddress).call()
console.log('allowance', allowance)
// if (parseInt(allowance) < params) {
let gasApprove = await contractFT.methods.approve(claimWlAddress, price).estimateGas();
gasApprove = (parseInt(gasApprove) * GAS_BOOST) | 0;
await contractFT.methods.approve(claimWlAddress, price).send({ gas: gasApprove, gasPrice });
// }
// return
const contract = new web3.eth.Contract(abis['CLAIMWL'].abi, claimWlAddress, {
from: myAddress
});
let gas = await contract.methods.claim(params).estimateGas()
gas = (parseInt(gas) * GAS_BOOST) | 0;
// let abc = await contract.methods.claim(params).send({gas, gasPrice})
// console.log(abc)
return contract.methods.claim(params).send({gas, gasPrice})
}
}
// 查询已Mint NFT数量
export const balanceOfAmount = async(name, myAddress) => {
let web3
if(name == 'ethereum') {
web3 = new Web3(window.ethereum);
} else if(name == 'okxwallet') {
web3 = new Web3(okxwallet)
} else if(name == 'connect') {
web3 = new Web3(provider)
}
let contract = new web3.eth.Contract(abis['CLAIM'].abi, claimAddress, { from: myAddress})
const tokenId = await contract.methods.totalCount(myAddress).call();
return parseInt(tokenId)
}
// mint配置
export const mintConfig = async(name, myAddress) => {
let web3
if(name == 'ethereum') {
web3 = new Web3(window.ethereum);
} else if(name == 'okxwallet') {
web3 = new Web3(okxwallet)
} else if(name == 'connect') {
web3 = new Web3(provider)
}
let contract = new web3.eth.Contract(abis['CLAIM'].abi, claimAddress, { from: myAddress})
const mintArr = await contract.methods.mintConfig(myAddress).call();
return mintArr
}
// 当前用户白单数量
export const fetchWLCount = async(name, myAddress) => {
let web3
if(name == 'ethereum') {
web3 = new Web3(window.ethereum);
} else if(name == 'okxwallet') {
web3 = new Web3(okxwallet)
} else if(name == 'connect') {
web3 = new Web3(provider)
}
let contract = new web3.eth.Contract(abis['CLAIMWL'].abi, claimWlAddress, { from: myAddress})
const tokenId = await contract.methods.whiteCount(myAddress).call();
return parseInt(tokenId)
const data = '0xe65ee803'
return queryMintData(user, data)
}
// 当前用户已获得的数量
export const fetchMintedCount = async (name, myAddress) => {
let web3
if(name == 'ethereum') {
web3 = new Web3(window.ethereum);
} else if(name == 'okxwallet') {
web3 = new Web3(okxwallet)
} else if(name == 'connect') {
web3 = new Web3(provider)
}
let contract = new web3.eth.Contract(abis['CLAIMWL'].abi, claimWlAddress, { from: myAddress})
const tokenId = await contract.methods.mintedNft(myAddress).call();
console.log(tokenId)
return tokenId
const data = '0xe65ee803'
return queryMintData(user, data)
}
// passport send()
const returnSend = async (web3, ERCAddress, contractAddress, abi, data) => {
// let gasPrice = await web3.request({ method: 'eth_gasPrice' })
let web3Provider = new providers.Web3Provider(web3)
const signer = web3Provider.getSigner();
const contract = new ethers.Contract(ERCAddress, abi, signer);
let gasApprove = await contract.estimateGas.approve(contractAddress, data)
// let gasApprove = tx
gasApprove = (parseInt(gasApprove) * GAS_BOOST) | 0;
console.log(parseInt(gasApprove),'---')
return contract.approve(contractAddress, data)
}

View File

@ -0,0 +1,24 @@
import { config, passport } from '@imtbl/sdk';
const passportInstance = new passport.Passport({
baseConfig: {
environment: config.Environment.SANDBOX, // or Environment.PRODUCTION
publishableKey: 'pk_imapik-test-1E-detBXtG7U$5961WuL', // replace with your publishable API key from Hub
// publishableKey: '', // replace with your publishable API key from Hub
},
clientId: 'gYQrjtct7CN4UVwDG2sb8yACfYRo3xQJ', // replace with your client ID from Hub
redirectUri: 'http://localhost:5173/redirect', // replace with one of your redirect URIs from Hub
logoutRedirectUri: 'http://localhost:5173/logout', // replace with one of your logout URIs from Hub
audience: 'platform_api',
scope: 'openid offline_access email transact',
popupOverlayOptions: {
disableGenericPopupOverlay: true, // Set to true to disable the generic pop-up overlay
disableBlockedPopupOverlay: true, // Set to true to disable the blocked pop-up overlay
}
});
const passportInit = app => {
app.component('passportInit',passportInstance)
app.config.globalProperties.$passportInit = passportInstance
// app.provide('passportInit', passportInit)
}
passportInstance.loginCallback()
export default passportInit

59
vite.config.js Normal file
View File

@ -0,0 +1,59 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import {nodePolyfills} from 'vite-plugin-node-polyfills'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
nodePolyfills({
globals: {
Buffer: false
}
}),
],
base: './',
// build: {
// // 打包输出目录
// outDir: 'dist',
// // 打包文件名
// assetsDir: 'static',
// sourcemap: false, // 禁用源映射
// // 静态资源引用路径
// assetsPublicPath: '/',
// // 是否开启压缩
// minify: 'terser',
// // 分离代码
// // splitChunks: {},
// // 环境变量
// env: {
// BASE_URL: process.env.VITE_API_BASE_URL
// },
// rollupOptions: {
// plugins: [nodePolyfills()],
// },
// commonjsOptions: {
// transformMixedEsModules: true,
// }
// },
define: {
global: {},
'process.env': {}
},
server: {
port: 5173, // 设置服务启动端口号
open: false, // 设置服务启动时是否自动打开浏览器
cors: true, // 允许跨域
// host: 'localhost', //
hmr:true,
// 设置代理,根据我们项目实际情况配置
proxy: {
'/api': {
target: process.env.VITE_BASE_API, // 将 /api 开头的请求代理到该地址
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace('/api/', '/')
}
}
}
})