Skip to content

Commit

Permalink
Update frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
icyfry committed Apr 12, 2024
1 parent 75c9cfc commit f37c29a
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 48 deletions.
52 changes: 37 additions & 15 deletions dapp/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import CryptoZombiesView from './components/CryptoZombiesView.vue'
import FundMeView from './components/FundMeView.vue'
import { MetaMaskInpageProvider } from "@metamask/providers";
import { onBeforeMount, ref } from 'vue'
import { Web3Utils } from './utils/web3utils.ts';
Expand All @@ -9,34 +10,55 @@ declare global {
var ethereum: MetaMaskInpageProvider;
}
onBeforeMount(() => {
initializeApp();
});
const web3Interact: any = ref<Web3Utils>(new Web3Utils(window.ethereum));
function initializeApp() {
console.log('App is initializing...');
web3Interact.value.ethInit();
onBeforeMount((): void => {
initializeApp();
});
function initializeApp(): void {
web3Interact.value.initialize();
}
window.ethereum.on('accountsChanged', () => initializeApp());
window.ethereum.on('accountsChanged', (): void => initializeApp());
</script>

<template>
<div>
<img src="/cryptozombies.png" class="logo" alt="Vite logo" />
<h1>
Test Frontend
<a href="https://github.com/icyfry/sandbox-foundry" target="_blank" rel="noopener">sandbox-foundry
</a>
</h1>
<h1>
Test DAPP
</h1>
<div class="tech">
<ul>
<li>NetworkId : {{ web3Interact.networkId }}</li>
<li>Account : {{ web3Interact.account }}</li>
<li v-if="web3Interact.cryptoZombiesContract !== undefined">CryptoZombies contract : {{
web3Interact.cryptoZombiesContract.options.address }}</li>
<li v-if="web3Interact.fundmeContract !== undefined">FundMe contract : {{
web3Interact.fundmeContract.options.address }}</li>
</ul>
</div>
<h2>CryptoZombies</h2>
<button v-on:click="web3Interact.createRandomZombie('test')">Create a Random Zombie</button>
<CryptoZombiesView v-if="web3Interact.account !== undefined" :web3Interact="web3Interact" />
<CryptoZombiesView v-if="web3Interact.cryptoZombiesContract !== undefined" :web3Interact="web3Interact" />
<h2>FundMe</h2>
<button v-on:click="web3Interact.fund(100)">Fund 100</button>
<button v-on:click="web3Interact.withdraw()">withdraw</button>
<FundMeView v-if="web3Interact.fundmeContract !== undefined" :web3Interact="web3Interact" />
</template>

<style scoped>
.tech {
background-color: #616161;
font-family: 'Courier New', Courier, monospace;
text-align: left;
}
.tech ul {
list-style-type: none;
}
.logo {
height: 4em;
padding: 1.5em;
Expand Down
18 changes: 7 additions & 11 deletions dapp/src/components/CryptoZombiesView.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { onMounted, ref } from 'vue'
import { Zombie, Web3Utils } from '../utils/web3utils.ts';
const props = defineProps<{ web3Interact: Web3Utils }>();
var zombies: Zombie[]
var error: string
let zombies = ref<Zombie[]>([]);
onMounted(() => {
zombies = props.web3Interact.getZombiesForAccount()
props.web3Interact.getZombiesForAccount().then((value) => {
zombies.value = value;
}).catch((err) => {
console.log(err)
})
})
</script>

Expand All @@ -21,9 +24,6 @@ onMounted(() => {
</li>
</ul>
</div>
<div class="error">
{{ error }}
</div>
</template>

<style scoped>
Expand All @@ -34,8 +34,4 @@ onMounted(() => {
.zombieview li {
list-style-type: none;
}
.error {
background-color: #a70000;
}
</style>
33 changes: 33 additions & 0 deletions dapp/src/components/FundMeView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { Web3Utils } from '../utils/web3utils.ts';
const props = defineProps<{ web3Interact: Web3Utils }>();
let balance = ref<string>('');
onMounted(() => {
props.web3Interact.getBalance().then((value) => {
balance.value = value;
}).catch((err) => {
console.log(err)
})
})
</script>

<template>
<div class="fmview">
Balance : {{ balance }}
</div>

</template>

<style scoped>
.fmview {
background-color: #616161;
}
.fmview li {
list-style-type: none;
}
</style>
2 changes: 1 addition & 1 deletion dapp/src/tests/web3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { Web3Utils } from '../utils/web3utils';

test('init without provider', () => {
const web3: Web3Utils = new Web3Utils(undefined)
expect(web3.ethInit()).toBe(false)
expect(web3.initialize()).toBe(false)
})
171 changes: 150 additions & 21 deletions dapp/src/utils/web3utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { MetaMaskInpageProvider } from "@metamask/providers";
import Web3, { EventLog, Contract } from 'web3';
import cryptozombiesABI from '../../abi/zombieownership.sol/ZombieOwnership.json';
import Web3, { Contract, Web3ContractError } from 'web3';

// Foundry generated contracts and broadcasts
import cryptozombiesABI from '../../../contracts/out/zombieownership.sol/ZombieOwnership.json';
import fundmeABI from '../../../contracts/out/FundMe.sol/FundMe.json';
import cryptozombiesBroadcastLocal from '../../../contracts/broadcast/DeployZombieOwnership.s.sol/31337/run-latest.json';
import fundmeBroadcastLocal from '../../../contracts/broadcast/DeployFundMe.s.sol/31337/run-latest.json';
import fundmeBroadcastSepolia from '../../../contracts/broadcast/DeployFundMe.s.sol/11155111/run-latest.json';

export class Web3Utils {

private web3js!: Web3;
private ethereum!: MetaMaskInpageProvider;
private readonly cryptoZombiesContractAddress: string = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512";
public account!: string;
public networkId!: bigint;
public cryptoZombiesContract!: Contract<any>;
public fundmeContract!: Contract<any>;

// Foundry broadcast data
private BROADCAST: { [key: number]: any } = {
1: { // Mainnet
cryptoZombies: null,
fundme: null
},
11155111: { // Sepolia
cryptoZombies: fundmeBroadcastSepolia,
fundme: fundmeBroadcastSepolia
},
31337: { // Local
cryptoZombies: cryptozombiesBroadcastLocal,
fundme: fundmeBroadcastLocal
},
};

constructor(ethereum: MetaMaskInpageProvider | undefined) {
if (ethereum) {
Expand All @@ -19,24 +42,52 @@ export class Web3Utils {
}
}

ethInit(): boolean {
if (this.ethereum == undefined) return false;
console.log("Init Web 3");
this.ethereum.request({ method: 'eth_requestAccounts' });
this.web3js = new Web3(this.ethereum);
this.web3js.eth.getAccounts().then((accounts: string[]) => {
this.account = this.web3js.utils.toChecksumAddress(accounts[0]);
})
this.cryptoZombiesContract = new this.web3js.eth.Contract(cryptozombiesABI.abi, this.cryptoZombiesContractAddress);

this.cryptoZombiesContract.events.NewZombie()
.on("data", (event: EventLog) => {
let zombie = event.returnValues;
console.log("A new zombie was born!", zombie.zombieId, zombie.name, zombie.dna);
})
//.on("error", console.error);
initialize(): void {
try {

console.log("Initialize web 3 utils");

if (this.ethereum == undefined) throw new Error("Provider not found");

this.ethereum.request({ method: 'eth_requestAccounts' });
this.web3js = new Web3(this.ethereum);

this.web3js.eth.getAccounts().then((accounts) => {
this.account = this.web3js.utils.toChecksumAddress(accounts[0]);
console.log("Account: " + this.account);
});

this.web3js.eth.net.getId().then((id: bigint) => {
this.networkId = id
const cryptozombiesContractAddress = this.getCryptozombiesContractAddress(this.networkId) as string
const fundmeContractAddress = this.getFundmeContractAddress(this.networkId) as string
this.cryptoZombiesContract = new this.web3js.eth.Contract(cryptozombiesABI.abi, cryptozombiesContractAddress);
this.fundmeContract = new this.web3js.eth.Contract(fundmeABI.abi, fundmeContractAddress);
});

return true;
} catch (error) {
throw new Error("Error during initialize Web 3 " + this.networkId + " " + this.account);
}
}

getFundmeContractAddress(networkId: bigint): string | null {
if (this.BROADCAST[Number(networkId)]?.fundme == null) throw new Error("No contract found for network " + networkId);
for (const transaction of this.BROADCAST[Number(networkId)].fundme.transactions) {
if (transaction.contractName === "FundMe") {
return transaction.contractAddress;
}
}
return null;
}

getCryptozombiesContractAddress(networkId: bigint): string | null {
if (this.BROADCAST[Number(networkId)]?.cryptoZombies == null) throw new Error("No contract found for network " + networkId);
for (const transaction of this.BROADCAST[Number(networkId)].cryptoZombies.transactions) {
if (transaction.contractName === "ZombieOwnership") {
return transaction.contractAddress;
}
}
return null;
}

createRandomZombie(name: string) {
Expand All @@ -53,7 +104,10 @@ export class Web3Utils {
});
}

getZombiesForAccount(): Zombie[] {
async getZombiesForAccount(): Promise<Zombie[]> {

if (this.cryptoZombiesContract == null) { throw new Error("Contract not initialized"); }

const zombies: Zombie[] = [];
console.log("getZombiesByOwner " + this.account);
this.cryptoZombiesContract.methods.getZombiesByOwner(this.account).call().then(
Expand All @@ -78,6 +132,81 @@ export class Web3Utils {
return this.cryptoZombiesContract.methods.zombieToOwner(id).call();
}

withdraw() {
console.log(`Withdrawing...`)
try {
const transaction = this.fundmeContract.methods.withdraw();

transaction.send({ from: this.account })
.on('transactionHash', function (hash: string) {
console.log(hash);
})
.on('confirmation', function (confirmation: { confirmations: bigint, receipt: { transactionHash: string, transactionIndex: bigint, blockHash: string, blockNumber: bigint, from: string }, latestBlockHash: string }) {
console.log(confirmation);
})
.on('receipt', function (receipt: any) { // type ReceiptOutput
console.log(receipt);
})
.on('error', function (error: Web3ContractError) {
console.log(error);
}).then(function (receipt: any) { // type ReceiptOutput
console.log(receipt)
})
.catch((error: any) => {
console.error(error);
});

} catch (error) {
console.error(error)
}
}

fund(ethAmount: any) {
console.log(`Funding with ${ethAmount}`)
try {

const transaction = this.fundmeContract.methods.fund({
value: this.web3js.utils.toWei(ethAmount, "ether"),
});

transaction.send({ from: this.account })
.on('transactionHash', function (hash: string) {
console.log(hash);
})
.on('confirmation', function (confirmation: { confirmations: bigint, receipt: { transactionHash: string, transactionIndex: bigint, blockHash: string, blockNumber: bigint, from: string }, latestBlockHash: string }) {
console.log(confirmation);
})
.on('receipt', function (receipt: any) { // type ReceiptOutput
console.log(receipt);
})
.on('error', function (error: Web3ContractError) {
console.log(error);
})
.then(function (receipt: any) { // type ReceiptOutput
console.log(receipt)
})
.catch((error: any) => {
console.error(error);
});

} catch (error) {
console.error(error)
}
}

async getBalance(): Promise<string> {
let res: string = "?"
try {
const balance = await this.web3js.eth.getBalance(String(this.fundmeContract.options.address))
res = this.web3js.utils.fromWei(balance, "ether")
} catch (error) {
console.log(error)
}
return res;
}



}
export interface Zombie {
name?: string;
Expand Down

0 comments on commit f37c29a

Please sign in to comment.