This commit is contained in:
huangjinming 2023-04-26 16:10:34 +08:00
parent 740e3694b4
commit c2a22d32ca
12 changed files with 4606 additions and 2784 deletions

451
README.md
View File

@ -1,3 +1,454 @@
运行代码指令
npm run dev
yarn run dev
const ERC20ABI = [
{
inputs: [
{
internalType: "address",
name: "_nftTarget",
type: "address"
},
{
internalType: "address[]",
name: "_manageAddress",
type: "address[]"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "user",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "nft",
type: "address"
},
{
indexed: false,
internalType: "uint256[]",
name: "nftSIds",
type: "uint256[]"
},
{
indexed: false,
internalType: "uint256[]",
name: "nftTIds",
type: "uint256[]"
}
],
name: "Minted",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes32",
name: "previousAdminRole",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes32",
name: "newAdminRole",
type: "bytes32"
}
],
name: "RoleAdminChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "account",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
}
],
name: "RoleGranted",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "account",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "sender",
type: "address"
}
],
name: "RoleRevoked",
type: "event"
},
{
inputs: [],
name: "DEFAULT_ADMIN_ROLE",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [],
name: "MANAGE_ROLE",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
}
],
name: "getRoleAdmin",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "uint256",
name: "index",
type: "uint256"
}
],
name: "getRoleMember",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
}
],
name: "getRoleMemberCount",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "grantRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "hasRole",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [],
name: "nft",
outputs: [
{
internalType: "contract IBEERC721",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "nftMinted",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "ownerToNFTs",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "renounceRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "role",
type: "bytes32"
},
{
internalType: "address",
name: "account",
type: "address"
}
],
name: "revokeRole",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes4",
name: "interfaceId",
type: "bytes4"
}
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
},
{
internalType: "uint256",
name: "count",
type: "uint256"
}
],
name: "mintToUser",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
},
{
internalType: "uint256[]",
name: "_nftIds",
type: "uint256[]"
}
],
name: "addNFTData",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
}
],
name: "getMintableCount",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function",
constant: true
},
{
inputs: [
{
internalType: "address",
name: "_user",
type: "address"
}
],
name: "getMintableNftIds",
outputs: [
{
internalType: "uint256[]",
name: "",
type: "uint256[]"
}
],
stateMutability: "view",
type: "function",
constant: true
}
]

View File

@ -1,3 +1,4 @@
import request from "@/utils/request";
/**
* begin claim badge
* @param {*} data: {id: string}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -11,6 +11,7 @@ import {
ACTIVATE_PROXY_ABI,
MYSTERY_BOX_ABI,
MYSTERY_PROXY_ABI,
MEDAL_REWARDS_ABI,
} from "@/configs/contracts";
const AppModule = useAppStore(pinia);
@ -115,6 +116,7 @@ export default class ChainManager {
console.log("balance: ", balance);
return balance;
}
/**
* get amount of mystery boxes

View File

@ -6,9 +6,10 @@ import { useUserStore } from "@/store/user";
import { isMobile } from "@/utils/resize";
import { hasMetamask, toHexChainId } from "@/utils/chain.util";
import { AllChains } from "@/configs/allchain";
import { ERC20ABI } from "@/configs/contracts";
import { ERC20ABI, LIMIT_ABI, MEDAL_REWARDS_ABI } from "@/configs/contracts";
import { TransactionReceipt } from "web3-core";
import { useEventBus } from "@vueuse/core";
import { Global } from "@/configs/global";
import * as bus_event from "@/bus/event";
import pinia from "@/store";
@ -95,6 +96,7 @@ export class Blockchain {
return;
}
this.web3 = new Web3(this.provider);
const chainId = await this.web3.eth.getChainId();
await this.checkChain(chainId);
@ -126,7 +128,7 @@ export class Blockchain {
* @private
*/
private async checkChain(chainId: number) {
if (!this.chainMap.has(chainId)) {
if (chainId !== AVAILABLE_CHAINS[0]) {
// if (this.walletType === 1) {
try {
await this.selectChain();
@ -195,19 +197,12 @@ export class Blockchain {
*/
private selectChain(): Promise<number> {
return new Promise((resolve, reject) => {
busNeedChangeChain.emit({
confirm: async (id: number) => {
console.log("select chain: ", id);
this.currentChain = id;
if (this.provider) {
await this.switchEthereumChain();
}
resolve && resolve(id);
},
cancel: (reason: any) => {
console.log("cancel select chain: ", reason);
reject && reject(reason);
},
this.switchEthereumChain(AVAILABLE_CHAINS[0], (res: any)=>{
if (res.err) {
reject && reject(res.err)
return
}
resolve && resolve(res.chain)
});
});
}
@ -256,6 +251,11 @@ export class Blockchain {
}
public async getContractInstance(address: string, abi: any = ERC20ABI) {
if (!this.web3) {
throw new Error(
"Web3 instance is not initialized. Please call `initWeb3` method after user login."
);
}
if (!this.instanceCacheMap.has(address)) {
const instance = new this.web3.eth.Contract(abi, address, {
from: AppModule.accountId,
@ -344,49 +344,38 @@ export class Blockchain {
* @param {() => void} cb
* @return {Promise<void>}
*/
async switchEthereumChain(chainId?: number, cb?: () => void) {
chainId = chainId || this.currentChain;
async switchEthereumChain(chainId?: number, cb?: (res: any) => void) {
chainId = chainId || AVAILABLE_CHAINS[0];
const hexChainId = toHexChainId(chainId);
const onChainChange = (chainId: string) => {
console.log("switchEthereumChain: ", chainId);
this.provider.removeListener("chainChanged", onChainChange);
cb && cb();
cb && cb({chain: chainId});
};
this.provider.on("chainChanged", onChainChange);
try {
const data = this.chainMap.get(chainId)!;
await this.provider.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: hexChainId }],
method: "wallet_addEthereumChain",
params: [
{
chainId: hexChainId,
chainName: data.name,
nativeCurrency: {
name: data.symbol,
symbol: data.symbol,
decimals: data.decimals || 18,
},
blockExplorerUrls: [data.explorerurl],
rpcUrls: [data.rpc],
},
],
});
console.log("switch chain success");
} catch (e: any) {
console.log("switch chain error: ", e);
if (e.code === 4902 || e.message.indexOf("Unrecognized chain ID") >= 0) {
try {
const data = this.chainMap.get(chainId)!;
await this.provider.request({
method: "wallet_addEthereumChain",
params: [
{
chainId: hexChainId,
chainName: data.name,
nativeCurrency: {
name: data.symbol,
symbol: data.symbol,
decimals: data.decimals || 18,
},
blockExplorerUrls: [data.explorerurl],
rpcUrls: [data.rpc],
},
],
});
console.log("add chain success");
} catch (addError) {
console.error("add chain error: ", addError);
this.provider.removeListener("chainChanged", onChainChange);
}
}
// console.error(e)
console.log("add chain success");
} catch (addError) {
console.error("add chain error: ", addError);
this.provider.removeListener("chainChanged", onChainChange);
cb && cb({err: addError})
}
}
@ -455,6 +444,38 @@ export class Blockchain {
console.log("increaseAllowance: ", res);
}
public async totalSupply(address: string) {
const coinInstance: any = await this.getContractInstance(
Global.LIMIT_ABI_Address,
LIMIT_ABI
);
const res = await coinInstance.methods.totalSupply().call({ gas: 1000000 });
console.log("totalSupply: ", res);
return res
}
// 获取当前用户可mint的数量
public async getMintableCount(address?: string) {
// address = address || //TODO: current address
const coinInstance: any = await this.getContractInstance(
Global.MEDAL_REWARDS_Address,
MEDAL_REWARDS_ABI
);
const res = await coinInstance.methods
.getMintableCount(AppModule.accountId)
.call({ gas: 1000000 });
console.log("getMintableCount: ", res);
return res
}
// 获取当前用户的mint总数
public async supplyLimit(address: string) {
const coinInstance: any = await this.getContractInstance(
Global.LIMIT_ABI_Address,
LIMIT_ABI
);
const res = await coinInstance.methods.supplyLimit().call({ gas: 1000000 });
console.log("supplyLimit: ", res);
return res
}
/**
* @param {string} address
* @param {string | null} account

View File

@ -47,7 +47,7 @@ import { useCopyToClipboard } from "./../../hooks/useCopyToClipboard";
const AppModule = useAppStore();
const chain = useChainStore();
const app = useAppStore();
const emit = defineEmits(['login-success'])
const { copied, copyToClipboard } = useCopyToClipboard(AppModule.accountId);
const message = copied.value
@ -64,14 +64,16 @@ const formatAddress = computed(() => {
async function login(event) {
if (!chain.logined) {
await chain.chainManager.login();
chain.logined = chain.chainManager.isLogined;
emit('login-success');
}
}
const logout = async () => {
await chain.chainManager.logout();
chain.logined = chain.chainManager.isLogined;
};
</script>

View File

@ -15,7 +15,7 @@ export const ALL_PROVIDERS = [
},
];
export const AVAILABLE_CHAINS = env === "production" ? [321] : [322];
export const AVAILABLE_CHAINS = env === "production" ? [137] : [80001];
export const OFFICE_ACCOUNT =
env === "production"

File diff suppressed because it is too large Load Diff

4
src/configs/global.ts Normal file
View File

@ -0,0 +1,4 @@
export namespace Global {
export const MEDAL_REWARDS_Address: string = "0xC0DA6D9d8AaDCFf7BD6d69B8b46948dD40BF0dec"; // 分发地址
export const LIMIT_ABI_Address: string = "0x5b36329D0DA1F56eD60F3C5DE1855c8dE0440140"; // 徽章地址
}

View File

@ -67,7 +67,7 @@ export const useUserStore = defineStore("user", () => {
if (!res.errcode && res.data?.token) {
accountId.value = account;
setToken(res.data.token);
glodata.token = getToken();
setAccountId(account);
AppModule.updateAccount(account);
AppModule.updateToken(res.data.token);
@ -83,6 +83,9 @@ export const useUserStore = defineStore("user", () => {
removeToken();
token.value = "";
accountId.value = "";
glodata.token = '';
glodata.accountId=''
AppModule.updateToken("");
AppModule.updateAccount("");
}

View File

@ -1,6 +1,6 @@
<template>
<div class="my-container">
<NavBar></NavBar>
<NavBar @login-success="fetchData"></NavBar>
<div class="content-top">
<div class="text-left">
<img src="../assets/img/badge/left-text.png" alt="" />
@ -20,7 +20,7 @@
<div class="card shadow-md rounded cards-container">
<div></div>
<div
class="card-title text-xl mb-8 text-yellow-color font-poppins-semiBoldItalic cards-container"
class="card-title text-xl text-yellow-color font-poppins-semiBoldItalic cards-container"
>
<div>GENESIS</div>
</div>
@ -41,7 +41,7 @@
</div>
</div>
<div class="genesicard">
<div class="genesis-title pt-24 font-firaSans-blackItalic">
<div class="genesis-title font-firaSans-blackItalic">
Genesis NFT <br />
holder Only
</div>
@ -61,20 +61,29 @@
</div>
<div class="ml-3">December 1 (st), 2022</div>
</div>
<div class="december-number">2000 / 4000</div>
<div class="december-number">
{{ totalSupply || "0" }} / {{ supplyLimit || "0" }}
</div>
<div
:class="isbtn === true ? 'disabled-claim' : 'claim'"
:class="mintableCount == 0 ? 'disabled-claim' : 'claim'"
@click="handClaimBadge"
>
<div class="claim-item">Claim your Airdorp</div>
</div>
<div class="dis-msg">
<div class="msg">Youre not eligible for this stage.<img class="rabbit" src="../assets/img/badge/rabbit.png" alt=""></div>
<div class="msg">
{{ message
}}<img
class="rabbit"
src="../assets/img/badge/rabbit.png"
alt=""
/>
</div>
</div>
</div>
<div class="card shadow-md rounded cards-container">
<div
class="text-xl font-bold font-poppins-semiBoldItalic card-title-right mb-4 p-3 text-black-color-300 cards-container"
class="text-xl font-bold font-poppins-semiBoldItalic card-title-right text-black-color-300 cards-container"
>
EXPLORER
</div>
@ -91,7 +100,7 @@
</div>
</div>
<div class="timeline">
<div class="timeline-boder flex justify-center pt-12">
<div class="timeline-boder flex justify-center pt-6">
<img src="../assets/img/badge/timeline.png" alt="" />
</div>
<div class="pt-2">
@ -103,6 +112,7 @@
<li>Beta Test I</li>
<li>Beta Test II</li>
</ul>
<div class="coming">Coming Soon</div>
</div>
</div>
</div>
@ -127,19 +137,25 @@ import {
import { useAppStore } from "@/store/app";
import { useRouter, useRoute } from "vue-router";
import { useMouse, useRafFn } from "@vueuse/core";
import { beginClaim, checkClaimStatus } from "../api/badge";
import { useMouseRotation } from "../hooks/useMouseRotation";
import NavBar from "@/components/layout/NavBar.vue";
import ImageTextModal from "../components/global/ImageTextModal.vue";
import { useChainStore } from "@/store/chain";
const AppModule = useAppStore();
const scrollOffset = ref("0%");
const parallaxFactor = 0.135; //
const visible = ref(false);
const isbtn = ref(false);
const canClaim = ref(false);
const chain = useChainStore();
const totalSupply = ref(0);
const mintableCount = ref(0);
const supplyLimit = ref(0);
const message = ref("");
const handlHero = () => {
visible.value = true;
};
const handleScroll = () => {
const currentScrollTop =
window.pageYOffset || document.documentElement.scrollTop;
@ -165,8 +181,62 @@ const {
stopTracking: stopTracking2,
} = useMouseRotation();
const handClaimBadge = () => {
isbtn.value = true;
const handClaimBadge = async () => {
if (mintableCount.value == 0) {
return false;
}
try {
if (!chain.logined) {
await chain.chainManager.login();
chain.logined = chain.chainManager.isLogined;
}
} catch (error) {
message.error("登录失败,请稍后重试");
console.error(error);
return;
}
try {
// beginClaim API
const res = await beginClaim(1);
// message.loading("...", 0);
console.log("领取失败,请稍后重试!", res.data.taskId);
// claim
let status = 0;
let timer;
while (status !== 9 && status !== 10) {
await new Promise((resolve) => {
timer = setTimeout(resolve, 5000); // 5
});
const resStatus = await checkClaimStatus(res.data.taskId);
status = resStatus.data.status;
console.log("status", resStatus.data.status);
}
// message.destroy(); // loading message
if (status === 9) {
console.log("领取成功!");
message.value = "claim success";
clearTimeout(timer); //
// message.success("");
} else if (status === 10) {
console.log("领取失败,请稍后重试!");
message.value = "claim failure";
clearTimeout(timer); //
// message.error("");
}
if (status === 0) {
message.value = "claiming !";
}
} catch (error) {
// message.destroy();
// message.error("");
console.log("领取过程中发生错误,请稍后重试!");
// clearTimeout(timer); //
console.error(error);
}
};
const bgTranslateX = ref(0);
@ -178,12 +248,34 @@ const handleScrolls = () => {
bgTranslateX.value -= scrollDelta / 3; //
scrollPos.value = newScrollPos;
};
const fetchData = async () => {
try {
//
totalSupply.value = await chain.chainManager.bc.totalSupply();
onMounted(() => {
//mint
mintableCount.value = await chain.chainManager.bc.getMintableCount();
mintableCount.value == 0
? (message.value = "Youre not eligible for this stage.")
: "";
//mint
supplyLimit.value = await chain.chainManager.bc.supplyLimit();
} catch (error) {
console.error("Error fetching data:", error);
}
console.log("totalSupply.value:", totalSupply.value);
console.log("mintableCount.value:", mintableCount.value);
console.log("supplyLimit.value:", supplyLimit.value);
};
//
onMounted(async () => {
if (AppModule.accountId) {
await fetchData();
}
window.addEventListener("scroll", handleScroll);
window.addEventListener("scroll", handleScrolls);
});
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("scroll", handleScrolls);
@ -207,7 +299,7 @@ onUnmounted(() => {
.star-bg {
position: absolute;
left: 30px;
top:30px;
top: 30px;
bottom: 0;
right: 20px;
z-index: -1;
@ -220,7 +312,7 @@ onUnmounted(() => {
.bg {
position: absolute;
left: 0;
top: 0;
top: -10px;
bottom: 0;
right: 0;
z-index: -2;
@ -233,7 +325,7 @@ onUnmounted(() => {
.text-left {
position: absolute;
left: 85px;
bottom: 30px;
bottom: 10px;
animation: slide-up 1s ease-out;
}
.text-right {
@ -250,10 +342,10 @@ onUnmounted(() => {
font-weight: 500;
color: #ffb900;
}
.timeline{
width:1188px;
.timeline {
width: 1188px;
margin-left: 5px;
height: 38px;
height: 38px;
}
.card {
transition: transform 0.1s ease;
@ -263,16 +355,32 @@ onUnmounted(() => {
}
.card-title {
font-size: 80px;
margin-bottom: 19px;
// font-family: "Poppins";
font-weight: normal;
color: #ffb900;
position: relative;
line-height: 60px;
background: linear-gradient(180deg, #f9fe1d 0%, #ffbb17 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.card-title::after {
content: "";
display: inline-block;
position: absolute;
right: 125px;
bottom: 3px;
width: 5px;
height: 5px;
background: #fcde1a;
margin-left: 5px;
vertical-align: middle;
}
.card-title-right {
font-size: 40px;
margin-top: 16px;
text-align: center;
// font-family: "Fira Sans";
font-weight: normal;
}
@ -307,7 +415,7 @@ p {
text-align: center;
line-height: 28px;
&:hover {
background: url("../assets/img/badge/more.png");
background: url("../assets/img/badge/more-hover.png");
}
}
.btn-more-right {
@ -324,6 +432,15 @@ p {
color: #a7a7a7;
text-align: center;
line-height: 28px;
.coming {
position: absolute;
top: -90px;
left: 20px;
font-size: 16px;
font-family: "Poppins-Regular";
font-weight: 400;
color: #a7a7a7;
}
}
.btn-more-right:hover .dropdown-menu {
display: block;
@ -336,6 +453,7 @@ p {
left: 0;
// background-color: white;
background: url("../assets/img/badge/dropdown-menu-bg.png") no-repeat;
background-size: 100% 100%;
min-width: 145px;
// box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
list-style-type: none;
@ -345,12 +463,20 @@ p {
}
.dropdown-menu li {
padding: 4px 10px;
padding: 2px 10px;
}
.dropdown-menu li::after {
content: "";
display: block;
border-bottom: 1px solid #838383;
margin-top: -1px; /* 调整下划线与文字之间的间距 */
}
.dropdown-menu li:hover {
// background-color: #f1f1f1;
}
.dropdown-menu li:last-child::after {
border-bottom: none;
}
.card-top {
width: 450px;
height: 450px;
@ -443,6 +569,7 @@ p {
.genesis-title {
font-size: 40px;
letter-spacing: 3px;
margin-top: 75px;
// font-family: "Fira Sans";
font-weight: normal;
text-transform: uppercase;
@ -452,7 +579,7 @@ p {
position: relative; /* 添加相对定位以便伪元素定位 */
width: 302px;
margin-left: 39px;
margin-right: 167px;
margin-right: 162px;
}
.genesicard::before {
@ -489,7 +616,7 @@ p {
font-family: Arial;
font-weight: 400;
color: #ffffff;
margin-bottom: 30px;
margin-bottom: 26px;
// line-height: 60px;
}
.disabled-claim {
@ -589,7 +716,6 @@ p {
height: 50px;
margin-top: 41px;
line-height: 50px;
font-size: 12px;
font-family: "Poppins";
font-weight: 400;
@ -597,12 +723,12 @@ p {
background: rgba(255, 255, 255, 0.04);
border: 1px solid #353535;
border-radius: 12px 0px 12px 0px;
.msg{
.msg {
margin-left: 21px;
position: relative;
.rabbit{
.rabbit {
position: absolute;
right: 0;
right: -6px;
top: -24px;
}
}

View File

@ -1,55 +1,57 @@
<!-- MedalApp.vue -->
<template>
<div>
<h1>可铸造的 NFT 数量{{ mintableCount }}</h1>
<button @click="mintNFTs">领取勋章</button>
</div>
</template>
<script>
import { ref } from 'vue';
import { ethers } from 'ethers';
import NftDistributorABI from './NftDistributorABI.json'; // ABI
export default {
setup() {
const mintableCount = ref(0);
const nftDistributorAddress = '0x123...'; //
const provider = new ethers.providers.Web3Provider(window.ethereum); // 使 MetaMask provider
const nftDistributorContract = new ethers.Contract(nftDistributorAddress, NftDistributorABI, provider);
const loadMintableCount = async () => {
try {
const userAddress = await provider.getSigner().getAddress(); //
const count = await nftDistributorContract.getMintableCount(userAddress);
mintableCount.value = count.toNumber();
} catch (error) {
console.error('Error fetching mintable count:', error);
}
};
const mintNFTs = async () => {
try {
const signer = provider.getSigner(); //
const userAddress = await signer.getAddress(); //
const contractWithSigner = nftDistributorContract.connect(signer);
const tx = await contractWithSigner.mintToUser(userAddress, mintableCount.value);
await tx.wait(); //
// NFT NFT
await loadMintableCount();
} catch (error) {
console.error('Error minting NFTs:', error);
}
};
// NFT
loadMintableCount();
return {
mintableCount,
mintNFTs,
};
},
};
</script>
<div>
<h1>可领取的勋章数量{{ mintableCount }}</h1>
<button @click="mintMedals">领取勋章</button>
</div>
</template>
<script>
import { ref } from 'vue';
import { ethers } from 'ethers';
import MEDAL_REWARDS_ABI from './MEDAL_REWARDS_ABI.json'; // ABI
export default {
setup() {
const mintableCount = ref(0);
const medalRewardsAddress = '0x123...'; //
const provider = new ethers.providers.Web3Provider(window.ethereum); // 使 MetaMask provider
const medalRewardsContract = new ethers.Contract(medalRewardsAddress, MEDAL_REWARDS_ABI, provider);
const loadMintableCount = async () => {
try {
const userAddress = await provider.getSigner().getAddress(); //
const count = await medalRewardsContract.getMintableCount(userAddress);
mintableCount.value = count.toNumber();
} catch (error) {
console.error('Error fetching mintable count:', error);
}
};
const mintMedals = async () => {
try {
const signer = provider.getSigner(); //
const userAddress = await signer.getAddress(); //
const contractWithSigner = medalRewardsContract.connect(signer);
const tx = await contractWithSigner.mintToUser(userAddress, mintableCount.value);
await tx.wait(); //
//
await loadMintableCount();
} catch (error) {
console.error('Error minting medals:', error);
}
};
//
loadMintableCount();
return {
mintableCount,
mintMedals,
};
},
};
</script>