עכשיו ממשיכים לחלק הכייפי וזה התקשורת של היוזרים באפליקציה עם החוזה שלכם! כל מה שצריך לעשות זה להקים איזו אפליקציה של צד לקוח, במקרה שלנו זה באר מים (יכול להיות כל דבר שתרצו). הרעיון של הבאר זה שאנשים יכולים להתחבר עם הארנק שלכם, לבקש ברכה מהבאר הקסומה, כך שהם "זורקים מטבע" לבאר והיא תברכך אותם. אז כתבתי קצת פרונט ב react ומכוון שזה לא קורס של react אז לא יהיו הסברים כלל בנושא, מה שכן יהיה קובץ אחד חשוב שעליו אסביר לפרטים שהוא מכיל בתוכו את התקשורת עם החוזה.
תקשורת עם החוזה וקוד ה front end
קודם כל יש את ספרייה שצריך להכיר שעושה עבורנו הרבה עבודה והיא ethers, נתקין אותה בתיקיית ה client שלנו כך:
npm i ethers
ועכשיו נציג את הקובץ שבו נתקשר עם החוזה שלנו:
import React, { useEffect, useState } from 'react'; import {ethers} from 'ethers'; import { contractABI, contractAddress, tokenContractAddress } from '../util/contracts'; export const TransactionContext = React.createContext<any>(null); declare var window: any const { ethereum } = window; const getEthereumContract = async () => { const provider = new ethers.BrowserProvider(ethereum); const signer = await provider.getSigner(); const transactionContract: any = new ethers.Contract(contractAddress, contractABI, signer); return transactionContract; } export const TransactionProvider = ({children}: any): JSX.Element => { const [walletConnectedAddress, setWalletConnectedAddress] = useState(); const [formData, setFormData] = useState({amount: '', message: ''}); const [isLoading, setIsLoading] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const addressTo = '0x508007f1c16Cd3533EE46a433C445E0560aA6404'; const handleChange = (e: any) => { console.log(formData); setFormData((prevState) => ({...prevState, [e.target.name]: e.target.value})); } const checkWalletConnected = async () => { if(!ethereum) return alert('Please install Metamask'); const accounts = await ethereum.request({ method: 'eth_accounts' }); if(accounts.length) { setWalletConnectedAddress(accounts[0]); } } const getWetTokens = async () => { try { const transactionContract = await getEthereumContract(); const { amount }: any = formData; const parsedAmount = ethers.parseEther(amount); const sendWET = await transactionContract.sendWET(tokenContractAddress, addressTo, parsedAmount); await sendWET.wait(); } catch (err) { console.log(err); setIsSuccess(false); throw new Error('No eth obj'); } } const sendTransaction = async () => { try { if(!ethereum) return alert('Please install Metamask'); const { amount, message }: any = formData; const transactionContract = await getEthereumContract(); const parsedAmount = ethers.parseEther(amount); await ethereum.request({ method: 'eth_sendTransaction', params: [{ from: walletConnectedAddress, to: addressTo, value: Number(amount * 1e18).toString(16), gasLimit: '0x5208', maxPriorityFeePerGas: '0x3b9aca00', maxFeePerGas: '0x2540be400' }] }); const transactionHash = await transactionContract.wellWishingPayment(addressTo, parsedAmount, amount, message); setIsLoading(true); await transactionHash.wait(); setIsLoading(false); setIsSuccess(true); } catch(e) { console.log(e); setIsSuccess(false); throw new Error('No eth obj'); } } useEffect(() => { checkWalletConnected(); }, []); return ( <TransactionContext.Provider value={{getEthereumContract, walletConnectedAddress, formData, setFormData, handleChange, sendTransaction, setIsLoading, isLoading, isSuccess, setIsSuccess, getWetTokens}}> {children} </TransactionContext.Provider> ) }
זהו קובץ של ריאקט המיועד לcontext שזה קובץ שמנהל את הסטייט הכללי של אפליקציה. אפשר לראות שיש הרבה דברים פה אבל מה שחשוב לנו לראות בפוסט זה הוא השימוש בפונקציות הללו:
getEthereumContract – פונקציה שחוזרת על עצמה בכל חוזה, למעשה כך מייבאים את האובייקט של חוזה שלנו וניתן לשלוט בפונקציות המוכלות בו.
sendTransaction – עושה שימוש בחוזה בפונקציה wellWishingPayment . פונקציה זו קיימת בחוזה שכתבו.
getWetTokens – פונקציה שגם עושה שימוש בחוזה שלנו, למעשה שולחת את הטוקן שיצרנו לכל יוזר שבחר להשתמש בבאר המשאלות.
החוזה של הפרויקט
אז אומנם דיברנו על הפרויקט פה ושם והראיתי דוגמאות שונות של חלקים חשובים אך לא הצגתי את כל החוזים של הפרויקט ומה הם עושים. אז למעשה בפרויקט שיצרתי יש שני חוזים, אחד ראינו, של הטוקן שיצרנו, ועוד חוזה שמשמש את הdapp , כך שיוזר יכול לשלוח איתריום לבאר המשאלות ולקבל ברכה, מלבד הברכה, יקבל היוזר בחזרה את הטוקן שיצרנו WET. להלן שני החוזים שעלו לפרויקט הזה:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; // import "../node_modules/hardhat/console.sol"; // import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; contract WellContract { address public owner; constructor() { owner = msg.sender; } address constant WET_TOKEN_ADDRESS = 0x2Fb19059f31a7d939470D55563ca9B19302606C5; event Transfer(address from, address receiver, uint amount, string message, uint timestemp, string keyword); event TransferSent(address _from, address _dest, uint _amount); struct TransferStruct { address sender; address receiver; uint amount; string message; uint timestemp; string keyword; } TransferStruct[] transactions; function getCount() public view returns(uint) { uint transactionsLength = transactions.length; return transactionsLength; } function wellWishingPayment(address payable receiver, uint amount, string memory message, string memory keyword) public { transactions.push(TransferStruct(msg.sender, receiver, amount, message, block.timestamp, keyword)); emit Transfer(msg.sender, receiver, amount, message, block.timestamp, keyword); } function sendWET(IERC20 token, address payable to, uint amount) public { // check the transaction successeed , then send WET uint lastIndex = transactions.length-1; require(transactions[lastIndex].sender == msg.sender, 'Cant get your WET tokens, Please try again'); token.transfer(to, amount); emit TransferSent(msg.sender, to, amount); } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.18; // Uncomment this line to use console.log import "../node_modules/hardhat/console.sol"; import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract WellToken is ERC20 { constructor(uint256 initialSupply) ERC20("WellToken", "WET") { _mint(msg.sender, initialSupply * 10 ** decimals()); } }
ניתן לראות את הפונקציות שהשתמשתי בהם בקובץ של react של ה client side ולאחר דיפלוי יש לנו אפשרות לגשת לפרטי החוזה, רק חשוב להציג עוד שני דברים חושבים מאוד והם:
import { contractABI, contractAddress, tokenContractAddress } from '../util/contracts';
החלק הזה מייבא מקובץ נוסף:
import abi from './Transactions.json'; // &&&& MUMBAI &&&& // WellContract החוזה של האפליקציה export const contractAddress = '<YOUR SMART CONTRACT ADDRESS>'; export const contractABI = abi.abi; // WellToken החוזה של הטוקן export const tokenContractAddress = '<YOUR SMART CONTRACT ADDRESS>';
אלו למעשה הכתובות של החוזים שעושים שימוש באפליקציה, כפי שהצגתי בשיעורים הקודמים, לאחר שעושים דיפלויי לחוזה, מקבלים את הכתובת שלו ואלו הכתובות שאנו רואים כאן. מעבר לזה יש גם את הABI שגם אותו אנחנו מקבלים לאחר דיפלוי ואותו גם נדרש לייבא על מנת לתקשר עם החוזה (זהו החוזה בצורת אובייקט והוא חשוב מאד על מנת לייצר תקשורת) .
כמו כן את החוזים העליתי לסביבה שנקראת MUMBAI שזה בעצם סביבת הטסט של POLYGON. סביבה מאד נוחה לעבודה וזולה . דיברתי עליה בזמנו בפוסט אחר של קורס בלוקציין – מעלים את החוזה ל-testnet
סיכום שיעור תקשורת עם החוזה החכם
אז זה כל הסיפור (על קצה המזלג 😘 ) . עברנו דרך כל השלבים ליצירת חוזה, העלאת החוזה ותקשורת עם החוזה. ובנינו פרויקט שמשלב בלוקציין כאפליקציית WEB3 . כמובן שיש עוד הרבה דברים חשובים כמו בדיקות לחוזים, אופטימיזציית גז, אבטחה ועוד דברים נוספים מורכבים , אך כל אלו יהיו רק בקורסים מתקדמים יותר.
כמו כן מי שרוצה להתנסות בפרויקט שיצרתי לטובת המדריך מוזמן להיכנס לאתר: blesson.co ולשחק עם זה , כרגע זה על סביבת הטסט אז אפשר לשחק עם כסף של טסט ולקבל ברכות מבאר המשאלות.
בכדאי להפוך למתכנת בלוקציין הבא, יהיה עליכם לעשות עוד עבודה רבה בעצמכם. נמשיך עם מילות סיום