From f37c29a25f4f8318ba424b3cea5462c1b5d56c92 Mon Sep 17 00:00:00 2001 From: Jonathan Le Brun <42697488+icyfry@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:17:21 +0200 Subject: [PATCH] Update frontend --- dapp/src/App.vue | 52 +++++-- dapp/src/components/CryptoZombiesView.vue | 18 +-- dapp/src/components/FundMeView.vue | 33 +++++ dapp/src/tests/web3.test.ts | 2 +- dapp/src/utils/web3utils.ts | 171 +++++++++++++++++++--- 5 files changed, 228 insertions(+), 48 deletions(-) create mode 100644 dapp/src/components/FundMeView.vue diff --git a/dapp/src/App.vue b/dapp/src/App.vue index aa092a2..4c8fcfa 100644 --- a/dapp/src/App.vue +++ b/dapp/src/App.vue @@ -1,5 +1,6 @@ diff --git a/dapp/src/components/FundMeView.vue b/dapp/src/components/FundMeView.vue new file mode 100644 index 0000000..ca6e56e --- /dev/null +++ b/dapp/src/components/FundMeView.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/dapp/src/tests/web3.test.ts b/dapp/src/tests/web3.test.ts index e165578..78cb55d 100644 --- a/dapp/src/tests/web3.test.ts +++ b/dapp/src/tests/web3.test.ts @@ -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) }) \ No newline at end of file diff --git a/dapp/src/utils/web3utils.ts b/dapp/src/utils/web3utils.ts index 35f5948..4ebb27d 100644 --- a/dapp/src/utils/web3utils.ts +++ b/dapp/src/utils/web3utils.ts @@ -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; + public fundmeContract!: Contract; + + // 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) { @@ -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) { @@ -53,7 +104,10 @@ export class Web3Utils { }); } - getZombiesForAccount(): Zombie[] { + async getZombiesForAccount(): Promise { + + 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( @@ -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 { + 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;