dApps | кнопка mint на сайте
Привет сегодня мы поговорим как сделать кнопку mint или просто списать деньги за покупку чего то на своем сайте через смарт-контракт.
Начну с небольших изменений к прошлой статье. Если не интересно, то можете скипнуть.
Я предлагаю создать компонент для кнопки check address, для более красивой записи и компактности кода.
export function CheckAddress({ funcAddress, checkAddr }) { return ( <div> <button onClick={funcAddress}> check address</button> <p>{checkAddr}</p> </div> ) }
Это наш компонент. И напомню, что в функцию CheckAddress мы передаем переменные props, то есть за переменную funcAddress будет отвечать наша функция, а за checkAddr будет отвечать наша глобальная переменная this.state.checkAddress.
Напомню, что компонент CheckAddress добавляем в нашу папку с компонентами.
И не забываем его импортировать в наш index.js
import { CheckAddress } from '../components/CheckAddress'
Добавляем эту строчку в начало index.js ко всем импортам.
Теперь этот компонент нужно объявить в нашей render функции. Удаляем старый код с кнопкой и пишем новый.
render() { if(!this.state.selectedAccount) { return <> <div> <ConnectWallet connectWallet={this._connectWallet} networkError={this.state.networkError} dismiss={this._dismissNetworkError}/> </div > </> } return( <> {this.state.balance && <p className={styles.balance}> balance: {ethers.utils.formatEther(this.state.balance).slice(0,10)} ETH</p>} <div> <CheckAddress funcAddress={this._checkAddress} checkAddr = {this.state.checkAddress}/> </div> </> ) }
Вот так выглядит теперь функция render. Отдельное внимание уделим строчке:
<CheckAddress funcAddress={this._checkAddress} checkAddr = {this.state.checkAddress}/>
Это и есть наш новый компонент, где funcAddress отвечает за функцию, а checkAddr за переменную, как я и говорил ранее.
Так же про компоненты: пишите с одинаковой буквы (заглавной или строчной) название файла и название функции, а то будет ругаться.
Так ну это было небольшое дополнение. Теперь к теме.
Изменение смарт контракта
Для начало добавим небольшие изменения в смарт-контракт.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; contract Lock { address[] public owner; mapping (address => uint) public amount; constructor() { owner.push(msg.sender); } modifier onlyOwner() { uint checkOwner = 0; for(uint i = 0; i <= owner.length; i++){ if(owner[i] == msg.sender){ checkOwner = i; break; } } require(owner[checkOwner] == msg.sender, "not an owner!"); _; } function getAddress() public view onlyOwner returns(address){ return msg.sender; } function mint() public payable{ require(msg.value >= 2000, "not anouth funds"); amount[msg.sender] += 1; } function makeOwner() public{ require(amount[msg.sender] >= 3, "error"); owner.push(msg.sender); } }
Массив из owner. Теперь у нас owner будет не один а массив из овнеров. То есть можно будет добавлять овнеров по мере выполнения каких-либо условий.
mapping который будет считать сколько раз адрес вызовет функцию mint и соответственно успешно отправлять деньги на наш смарт-контракт.
Изменение в constructor. Теперь в конструкторе у нас будет не owner = msg.sender, а owner.push(msg.sender), потому что теперь массив из овнеров.
Модификатор onlyOwner конечно же тоже нужно поменять.
Так как нам нужно понять, есть ли тот или иной адрес в массиве овнеорв, нужно будет бежать по каждому из элементов этого массива и проверять на совпадение. Если такой адрес нашелся равный msg.sender, то все ок если нет, то require, откатит транзакции.
Новая функция mint. Она как раз отвечает за перевод средств с аккаунта на адрес смарт контракта. Чтоб это сделать просто помечаем функцию как payable, ну добавляем условия минимального перевода для выполнения функции и после добавляем в mapping +1 успешный mint.
Функция makeOwner, будет делать наш адрес овнеров контракта, при условии, что мы сделали 3 минта или больше. То есть пушим наш адрес в массив овнеров.
Теперь можно запустить ноду и деплой смарт-контракта. Как это делать показывал в прошлых статьях про кнопку на сайте или подключение смарт-контракта к сайту. Если вы забыли или не знаете.
Новые функции в index.js фронтенд
Функция _mint
_mint = async() =>{ const tx = await this.Lock.mint({ value: "5000" }) await tx.wait() let addrBought = Number(await this.Lock.amount(this.state.selectedAccount)) this.setState({ boughtPass: addrBought }) await this.updateBalance() }
Как и в других функциях, мы вызываем транзакцию при помощи this.Lock.mint и
передаем туда в качестве аргумента value: 5000, что будет обозначать 5000 wei.
И соответственно передает эти деньги в смарт-контракт.
await tx.wait() - очень важная функция, которая дожидается, пока транзакция выполнится. И потом мы просто обращаемся к нашему mapping, чтоб вывести количество минтов нашего адреса.
в this.setState() мы передаем boughtPass - глобальная переменная, которую добавляем в конструктор (весь код конструктора ниже) и передаем ей amount - количество минтов.
Так же обязательно оборачиваем в Number наш вызов mapping, так как он будет выводиться как bigNumber и будет ошибка, если так не сделать.
Вызов updateBalance(). Чтоб наш баланс кошелка обновился на сайте. Нужно вызвать эту функции, которую мы уже написали в прошлых статьях.
Функция _makeOwner
_makeOwner = async() =>{ try{ let tx = await this.Lock.makeOwner() this.setState({ messageError: null }) await this.updateBalance() }catch{ this.setState({ messageError: "Now you can't stay owner" }) } }
Тут мы вызываем транзакцию на функцию makeOwner, и если транзакция проходит, то мы обнуляем messageError. И вызываем снова updateBalance()
messageError - новая глобальная переменная в конструкторе. Добавляем ее
constructor(props) { super(props) this.initialState = { selectedAccount: null, networkError: null, balance: null, checkAddress: null, boughtPass: null, messageError: null } this.state = this.initialState }
Если же не прошла транзакция, то мы выводим на экран сообщение об ошибке.(То, что в catch)
Новые компоненты
Создаем два новых компонента. Mint.js и MakeOwner.js
Теперь код Mint.js
export function Mint({mintFunc, passId}) { return ( <div> <button onClick={mintFunc}>mint</button> <p>{passId}</p> </div> ) }
Тут все также, как и в начале статьи с компонентом checkAddress.
Если вкратце, то переменные mintFunc, passId - наши props, то есть в ниx передаем функцию _mint и this.state.boughtPass
return( <> {this.state.balance && <p className={styles.balance}> balance: {ethers.utils.formatEther(this.state.balance).slice(0,10)} ETH</p>} <div> <CheckAddress funcAddress={this._checkAddress} checkAddr = {this.state.checkAddress}/> </div> <div> <Mint mintFunc= {this._mint} passId = {this.state.boughtPass}/> </div> </> )
Тут мы должны обратить внимание на
<Mint mintFunc= {this._mint} passId = {this.state.boughtPass}/>
Как я и говорил, тут две переменные, одна отвечает за _mint(), другая за this.state.boughtPass.
Так же это делать не обязательно, если вам не нравится работать с компонентами. Просто все что в компоненте переходит в index.js в return.
И не забываем импортировать в начало index.js наш компонент
import { Mint } from '../components/Mint'
Теперь код MakeOwner.js
export function MakeOwner({ funcMakeOwn, ErrMsg }) { return ( <div> <button onClick={funcMakeOwn}> make owner </button> <p> {ErrMsg}</p> </div> ) }
Тут ровно так же как и в mint.js по логике.
return( <> {this.state.balance && <p className={styles.balance}> balance: {ethers.utils.formatEther(this.state.balance).slice(0,10)} ETH</p>} <div> <CheckAddress funcAddress={this._checkAddress} checkAddr = {this.state.checkAddress}/> </div> <div> <Mint mintFunc= {this._mint} passId = {this.state.boughtPass}/> </div> <div> <MakeOwner funcMakeOwn={this._makeOwner} ErrMsg = {this.state.messageError} /> </div> </> )
Добавили так же как и в mint.js но для MakeOwner.js
<MakeOwner funcMakeOwn={this._makeOwner} ErrMsg={this.state.messageError} />
import { MakeOwner } from '../components/MakeOwner'
По факту все готово. По хорошему добавить в наш смарт-контракт функцию, которая будет забирать все деньги из контракта на наш адрес . Но это можете придумать сами.
Смотрим результат
Вызываем check Address и make owner
Как видно мы не овнеры контракта и не можем им стать пока что.
Теперь вызываем mint 3 раза, чтоб получить доступ к make owner.
Теперь мы вызвали check address и получили адрес аккаунта. Значит мы стали овнером контракта.
Если вы сделали все так же как я, то у вас тоже должно работать. Дальше больше. В целом с этими знаниями уже можно придумать кучу всяких идей и реализовать их. Удачи.
tg: мой телеграмчик)
github:
этот проект на гитхабе
все файлы из папки front в отдельном репозитории
репозиторий попки front