Trečiosios pakopos

    Taigi, jūs turite pagrindų. Ankstesniame skyriuje sukūrėte išmaniąją sutartį ir įdiegėte ją naudodami „Truffle“. Tačiau ankstesniame skyriuje jūsų išmaniosios sutartys buvo išdėstytos vietiniame kūrimo tinkle – ir tai nėra smagu, nes tik jūs galite diegti dalykus ir bendrauti su tuo vietiniu bandomuoju tinklu! Mes norime draugų! Ir prieiga prie kitų pažangių sutarčių, kurias įdiegė kiti žmonės!

    Todėl šiame skyriuje pereisime prie viešo „Ethereum“ testneto naudojimo, kad galėtumėte prisijungti prie visų veiksmų, vykstančių aplink „Ethereum“ ekosistemą.!

    Pradėkime!

    Pirmiausia kalbėsime apie tai, kaip jūs gaunate prieigą prie šių viešų „Ethereum“ tinklų.

    Norėdami pasiekti šiuos tinklus, turite prisijungti prie mazgo, kuris yra prijungtas prie atitinkamo tinklo. Kiekvieną „Ethereum“ tinklą galite žiūrėti kaip į savo mažą „Ethereum“ pasaulį, o „Ethereum“ mazgą galite peržiūrėti kaip savo vartus ar prieigos tašką į kiekvieną iš šių pasaulių! Kadangi „Ethereum“ yra paskirstytasis tinklas, kiekvienas „Ethereum“ mazgas saugo visą tinklo, prie kurio jis yra prijungtas, būseną (yra mazgų, kuriems nereikia išsaugoti visos būsenos, tačiau kol kas dėl to nesijaudinkite) ir nuolat bendrauja su kitais tinklo mazgais, kad ši būsena būtų atnaujinta! Todėl, norėdami skaityti ir rašyti iš šios būsenos, turėsime pasiekti vieną iš šių mazgų.

    Galite labai gerai prižiūrėti savo mazgą naudodami vieną iš daugelio šiuo metu prieinamų „Ethereum“ klientų („Hyperledger Besu“ („ConsenSys“ sukurtas „Java“ klientas), „Geth“ („Go“ klientas), „Parity“ („Rust“ klientas) ir kt.), Tačiau yra gana šiek tiek „DevOps“ pridėtinių išlaidų, susijusių su jūsų „Ethereum“ mazgo priegloba ir priežiūra – ypač jei norite tai padaryti patikimai! Mes, „ConsenSys“, sukūrėme „Infura“ – pasaulinės klasės „Ethereum“ infrastruktūros pasiūlymą. „Infura“ rūpinasi visa „mazgo valdymo“ dalimi, suteikdama jums greitą, patikimą ir keičiamą prieigą prie „Ethereum“ mazgų grupių! Galite manyti, kad „Infura“ yra „Ethereum-mazgai-kaip-paslauga“ &# 128578;

    „Infura“ pradžia

    Norėdami pradėti naudotis „Infura“, norėsite užregistruoti paskyrą infura.io. Nesijaudinkite – pradėti naudoti visiškai nemokamai, jums nereikės įvesti jokios neskelbtinos informacijos!

    Užregistravę būsite nukreipti į puslapį, kuris atrodo taip:

    infuraPrisijungti

    Kaip siūloma šiame puslapyje, norėdami pradėti, pasirinkite pirmąją parinktį „Pradėkite ir sukurkite savo pirmąjį projektą, kad galėtumėte prisijungti prie„ Ethereum “tinklo!“

    Savo projektą galite pavadinti kaip tik norite – mes pavadinsime „bandomąjį projektą“.

    „InfuraNP“

    Dabar jums bus pateikti kredencialai, kurių jums reikės norint pasiekti „Infura“ mazgus!

    InfuraC

    Laikykite šį puslapį atvirą! Vėliau prie jo grįšime &# 128578;

    Kitas dalykas, kurį atliksime, bus inicijuoti naują triufelių projektą. Jei jums reikia pagalbos diegiant „Truffle“, žiūrėkite ankstesnį šios dokumentacijos skyrių.

    Norėdami inicijuoti naują triufelių projektą, sukurkite naują aplanką ir paleiskite

    triufelių inic

    Tada norėsite pridėti „Truffle HD Wallet“ teikėją prie savo naujai inicijuoto projekto, kad galėtumėte pasirašyti savo operacijas, kol jos nebus išsiųstos į „Infura“ mazgus. Kiekvienas valstybės pakeitimas, kurį atliekate „Ethereum“, įvyksta kaip sandoris – nesvarbu, ar tai sutarties vykdymas, ar funkcijos iškvietimas sutartyje, ar ženklo siuntimas! Kiekviena operacija turi būti pasirašyta sąskaitos, todėl mūsų programai reikia galimybės pasirašyti operacijas, kad ji galėtų atlikti valstybės pakeitimus „Ethereum“!

    Kiekvienas sandoris taip pat kainuoja eterį. Ši sandorio kaina vadinama „dujų kaina“. Todėl, norėdami, kad mūsų pasirašytos operacijos būtų apdorojamos tinkle, kai tik jos bus išsiųstos į „Infura“ mazgus, turėsime finansuoti savo sąskaitą eteriu. Mes tai aptarsime šiek tiek vėliau, bet tai tik dar viena svarbi priežastis, kodėl jums reikės piniginės & piniginės tiekėjas!

    Norėdami pridėti „Truffle HD“ piniginės teikėją prie naujai inicijuoto projekto tipo savo terminale:

    npm install –save @ truffle / hdwallet-Provider

    Tai gali mesti kai kuriuos įspėjimus, bet kol jis įdiegiamas, jūs gerai einate!

    Dabar mes galime sukurti „Ethereum“ paskyrą, kurią naudosite savo programoje! Kadangi mūsų piniginės teikėjas yra HD (hierarchinė deterministinė) piniginė, galime deterministiškai sugeneruoti paskyras naudodami tą pačią pradinę frazę arba mnemoninę.

    Norėdami sukurti savo paskyrą, pirmiausia turėsime paleisti „Ganache“. „Ganache“ yra triufelių produktas, leidžiantis lengvai sukurti savo vietinį „dev“ tinklą. Norėdami paleisti ganache, tiesiog įveskite

    ganache-cli

    Jei atlikote šio vadovo 2 veiksmą, turėtumėte jau įdiegti „Ganache / ganache-cli“ – jei to nepadarėte, galite įdiegti naudodami komandą npm:

    npm įdiegti -g ganache-cli

    Arba jei naudojate siūlus 

    verpalai globaliai pridėti ganache-cli

    Tada turėsime leisti mūsų programai kalbėtis su „Ganache“. Eikite į savo projekto katalogą ir patikrinkite failą „truffle-config.js“, paprasčiausiai nepaisykite (arba pridėkite) šias eilutes tinkle:

    plėtra: {host: "127.0.0.1", // „Localhost“ (numatytasis: nėra) prievadas: 8545, // Standartinis „Ethereum“ prievadas (numatytasis: nė vienas) tinklo_ID: "*" // Bet koks tinklas (numatytasis: nėra)},

    nenuoseklus

    Malonu! Dabar mūsų programa gali susisiekti su mūsų „Ganache“ kūrimo tinklu, veikiančiu 127.0.0.1:8545! Dabar, naujame terminalo lange (bet vis tiek jūsų projekto aplanke), paleiskite komandą

    triufelių konsolė

     prisijungti prie „Ganache“ tinklo. Nesijaudinkite – vėliau prisijungsime prie viešojo tinklo! Mes tiesiog turime prisijungti prie „Ganache“ dabar, kad sukurtume raktus &# 128578;

    Pastaba: jei kyla problemų, įsitikinkite, kad „Ganache“ jūsų RPC serverio prievado numeris atitinka jūsų triufelio konfigūracijos failą. Numatytuoju atveju 8545 turėtų veikti, kitaip pakeiskite konfigūracijos failą, kad jis atitiktų „Ganache“.

    Dabar „Truffle“ konsolėje įveskite šias komandas, kad sukurtumėte savo piniginę:

    const HDWalletProvider = reikalauti (‘@ truffle / hdwallet-Provider’);

    Tai turėtų sukelti atsakymą „neapibrėžtas“

    Jei naudojate 12 žodžių mnemotiką, galite naudoti tokį mnemonikos generatorių kaip šitas jei norėtum!

    Įsitikinkite, ar išsaugojote MNEMONIN ((SĖKLOS) Frazę! Mums to prireiks vėliau &# 128515;

    Tada pridėkite šią komandą savo terminale (kol dar kuriate triufelius):

    const mnemonic = ’12 žodžių čia’; const piniginė = new HDWalletProvider (mnemonic, "http: // localhost: 8545");

    Dabar triufelių konsolėje įveskite komandą 

    piniginė

    Jei slinksite aukštyn, turėtumėte pamatyti panašų paskyrų sąrašą!

    priedas

    Nepaisant to, kad ši sąskaita sugeneruota, kai buvome prisijungę prie „Ganache“, tą pačią (-as) „Ethereum“ paskyrą (-as) galime naudoti bet kuriame „Ethereum“ tinkle (tačiau atkreipkite dėmesį, nors tą pačią sąskaitą galima naudoti bet kuriame „Ethereum“ tinkle, su tuo susijęs turtas / veikla paskyra yra susijusi su tinklu – pavyzdžiui, jei atliksiu operaciją „Ethereum Mainnet“, ši operacija bus vykdoma tik „Ethereum Mainnet“, o ne kitame tinkle). Dabar mes nustosime bendrauti su „Ganache“ (vietiniu „dev“ tinklu) ir pradėsime naudoti šią paskyrą sąveikai su kai kuriais viešuosius tinklus!!

    Paprastai pirmas dalykas, kurį turėsite atlikti bendraudami su viešuoju tinklu, yra gauti tam tikrą šio tinklo eterį. Mūsų atveju mes prisijungsime prie viešojo „Ropsten“ bandymų tinklo, todėl turėsime įsigyti šiek tiek Ropsteno eterio (ETH)! Nesijaudinkite – bandomasis grynasis ETH yra nemokamas ir gausus, jį gauti yra labai lengva &# 128077;

    Laikas įsigyti bandomąjį ETH

    Norėdami gauti šiek tiek Ropsten ETH, eikite į „Ropsten“ maišytuvas. Įklijuokite savo sąskaitos adresą ir altas! Gavote šiek tiek „Ropsten ETH“ ir galite pradėti siųsti operacijas (t. Y. Keisti būseną) „Ropsten“ tinkle!

    „Ropsten“ bandymų tinklas yra viešas „Ethereum“ testų tinklas, kuriame galite išbandyti savo kodą aplinkoje, kuri tiksliai atspindi „Ethereum“ tinklo tinklą. Pagrindinis skirtumas tarp „Ropsten“ bandymo tinklo (ir kitų viešų „Ethereum“ bandomųjų tinklų) yra tas, kad bandymų tinkle ETH yra daug ir neturi jokios realios vertės! Kai pradėsite bendrauti su „Ethereum“ mainnet, eteris, kurį naudojate mokėdami už savo operacijas (dujų sąnaudos), kainuos TIKRUS dolerius – taigi, mes turime įsitikinti, kad iš anksto darome viską teisingai, kad neprarastume sunkumų uždirbti pinigai / mūsų brangus mainnet ETH!

    „Ropsten“ bandymų tinkle, kaip ir daugumoje kitų viešų bandymų tinklų, yra daugybė blokatorių, kad galėtumėte peržiūrėti veiklą, vykstančią grandinėje (https://ropsten.etherscan.io/). Norėdami pamatyti savo finansuojamą sąskaitą, tiesiog įklijuokite savo paskyros adresą į naršyklę ir galėsite peržiūrėti visą su ja susijusią istoriją:

    Ekrano kadras 2020 09 01, 4 34 21 val

    Gerai! Dabar, kai turime savo piniginės tiekėją ir sąskaitą, finansuojamą iš „Ropsten ETH“, galime grįžti į savo projektą ir nukreipti jį į „Infura“ mazgus, sujungtus su „Ropsten“ bandymų tinklu..

    Pirmas dalykas, kurį norėsime padaryti, yra sukurti a.env failą, kuriame bus mūsų brangios PASLAPTYS! Šios paslaptys apima mūsų „Infura“ API raktą (sugeneruotą sukūrus „Infura“ paskyrą) ir mnemoninę frazę.

    Projekto šakniniame lygyje tiesiog sukurkite naują failą „.env“. Be to, turėsite įdiegti „dotenv NPM“ paketą, terminale įvesdami šią komandą

    npm įdiegti – išsaugoti dotenv

    Šiame „new.env“ faile jums reikės dviejų dalykų:

    INFURA_API_KEY = ĮVeskite savo API RAKTĄ ČIA (be citatų)

    MNEMONIC = “objektyvo banginio ventiliatoriaus vielos burbulas internetinėje sėdynėje atskleidžia akcijų numerio sakinio nugalėtoją”

    „INFURA_API_KEY“ yra projekto, kurį anksčiau sukūrėte „Infura“, projekto ID:

    Ekrano kadras 2020 09 01 4 37 12 val

    „MNEMONIC“ yra 12 žodžių pradinė frazė, kurią anksčiau naudojote kurdami savo sąskaitą.

    Dabar jūsų failas turėtų atrodyti taip:

    Ekrano kadras 2020 09 01, 4 41 53 val

    Gerai, mes artėjame!

    PASTABA: Jei ketinate tai perkelti į „Github“ saugyklą arba kaip nors paviešinti šį projektą, Įsitikinkite, kad jūsų.env failas yra in.gitignore, kad jūsų paslaptys nebūtų atskleistos.! 

    Dabar reikės pereiti prie failo truffle-config.js. Čia turėsime pridėti keletą dalykų, kad galėtume pažymėti mūsų paslaugų teikėją (kuris naudojamas sąveikai su „Infura“ (anksčiau įdiegtu „Truffle HDWallet“ teikėju), ir nukreipti programą į „Ropsten Infura“ mazgus..

    Failo viršuje pridėkite:

    reikalauti ("dotenv") .config (); const HDWalletProvider = reikalauti ("@ trumas / „hwwallet“ teikėjas");

    Tada skiltyje „tinklai“ norėsite pridėti šį tinklą:

    ropstenas: {teikėjas: () => naujas „HDWalletProvider“ (process.env.MNEMONIC, „https://ropsten.infura.io/v3/$ {process.env.INFURA_API_KEY}“), network_id: 3, // Ropsten id gas: 5500000, // Ropsten turi apatinė blokavimo riba nei „mainnet“ patvirtinimai: 2, // konf. laukti laukiant tarp diegimų. (numatytasis: 0) timeoutBlocks: 200, // blokų # skaičius prieš diegimo skirtąjį laiką (mažiausias / numatytasis: 50) skipDryRun: true // Praleisti sausą paleidimą prieš perkėlimus? (numatytasis: klaidingas viešiesiems tinklams)}

     

    Dabar failas „truffle-config.js“ turėtų atrodyti maždaug taip!

    Paraštės užrašas:

    Jei naudojate „Infura“ taškus, reikalingas parametras „nuo“, nes jie neturi piniginės. Jei naudojate „Ganache“ arba „Geth RPC“ taškus, tai yra pasirenkamas parametras.

    Ekrano kadras 2020 09 01, 4 50 54 val

    DABAR esame pasiruošę magijai! Atėjo laikas įdiegti išmanią sutartį ROPSTEN!

    Pažangios sutarties nustatymas

    Tvirtumo nustatymas

    Pirmiausia norėsime sukurti išmanią sutartį, skirtą naudoti! Galite paimti išmaniąją sutartį, kurią sukūrėte ankstesniame šio vadovo skyriuje, sukurti savo išmaniąją sutartį arba tiesiog naudoti šią (labai paprastą) pavyzdinę sutartį:

    pragmos tvirtumas >= 0,5,8; sudaryti sutartį „SimpleStorage“ {uint256 storageData; funkcijų rinkinys (uint256 x) public {storageData = x; } funkcija get () viešasis vaizdas grąžina (uint256) {grąžina saugomus duomenis; }}

    Ši sutartis turėtų būti sukurta kaip „.sol“ (solidumas) failas jūsų projekto aplanke „sutartys“ (pagal šį scenarijų sukūrėme failą „SimpleStorage.sol“, kuris yra mūsų „SimpleStorage“ sutartis:

    pastoliai

    Perkėlimo sąranka

    Tada turėsime nustatyti perkėlimo failą!

    Perkėlimai yra „JavaScript“ failai, padedantys diegti sutartis „Ethereum“ tinkle. Šie failai yra atsakingi už jūsų diegimo užduočių atlikimą ir yra parašyti darant prielaidą, kad laikui bėgant jūsų diegimo poreikiai pasikeis. Vystantis jūsų projektui, sukursite naujus perkėlimo scenarijus, kad galėtumėte toliau plėtoti šią blokų grandinę. Anksčiau vykdytų migracijų istorija įrašoma grandinėje pagal specialią migracijos sutartį. Galite rasti daugiau informacijos apie juos čia.

    Mūsų perkėlimo failas, skirtas naudoti sutartį, atrodys taip:

    const SimpleStorage = artefaktai.require ("„SimpleStorage.sol“"); module.exports = function (deployer) {deployer.deploy (SimpleStorage); };

    Išsaugokite šį failą aplanke „migrations“ pavadinimu „2_deploy_contracts.js“.

    Pirmojo viešojo pirkimo sutarties vykdymas

    Laikas migruoti

    Dabar faktiškai esate pasirengęs, kad VYKSTA MAGIJA! Grįžkite į konsolę ir įveskite

    triufeliai migruoja – tinklas ropstenas

    Bumas!&# 128163; Jūsų kodas buvo pritaikytas viešam „Ropsten Ethereum Test Net“!!! 

    Ką tik įvyko:

    1. Jūsų „Solidity“ išmanioji sutartis (aplanke „sutartys“) buvo sudaryta iki baitų kodo – kompiuterio skaitomo „Ethereum“ virtualiosios mašinos kodo, kurį reikia naudoti.

    2. Šis baitų kodas ir kai kurie kiti duomenys buvo sugrupuoti į operaciją.

    3. Tą operaciją pasirašė jūsų paskyra.

    4. Ta operacija buvo nusiųsta į „Infura“ mazgą, kuris yra prijungtas prie „Ropsten“.

    5. Sandoris buvo platinamas visame tinkle, jį pasiėmė „Ropsten“ kalnakasis ir įtraukė į „Ropsten“ bloką.

    6. Jūsų išmanioji sutartis dabar yra tiesioginė „Ropsten“ blokų grandinėje!

    Savo sutartį galite peržiūrėti naudodami „Etherscan“: https://ropsten.etherscan.io/ – tiesiog įklijuokite sutarties adresą (jis turėtų būti jūsų terminale), kad jį peržiūrėtumėte!

    Ekrano kadras 2020 09 01, 5 19 12 val

    Nuostabu! Mes ką tik įdiegėme savo pirmąją išmaniąją sutartį „PUBLIC Ethereum“ tinkle! &# 129327;

    Procesas yra tas pats diegiant į „Ethereum“ mainnet, išskyrus tai, kad tinkle failą truffle-config.js pakeisite „Ethereum“ mainnet (ir, žinoma, paleiskite „mainnet Truffle“ perkėlimo komandą, o ne „Ropsten“). ! Čia jums nebus atliktas šis procesas, nes dislokavimas „Ethereum“ maitinimo įstaigoje jums kainuos faktinius dolerius, bet jei norite gauti pagalbos, pereikite prie „ConsenSys“ nesantaika ir mes mielai pagelbėtume!

    „Web3 Frontend“ kūrimas 

    Dabar, kai mes išplėtėme savo sutartį su „Ropsten“, sukursime paprastą vartotojo sąsają, kad galėtume su ja bendrauti!

    Pastaba: „dApp“ „front-end“ yra tik jūsų kasdieniai, įprasti, seni front-endai – todėl mes galime naudoti visus savo senus įrankius, kurie mums yra žinomi (sukurti-reaguoti-programą ir pan.), Kad sukurtume savo priekinę dalį ir tada tiesiog pridėkite kelis dalykus, kad leistumėte sąsajai skaityti ir rašyti „Ethereum“! Tai reiškia, kad visi jūsų senieji žiniatinklio kūrimo įgūdžiai gali būti tiesiogiai perkelti į „Ethereum-land / Web3“!!

    Paspartinkite mūsų „React“ projektą 

    Gerai, pradėkime.

    Pirmiausia įsitikinkite, kad turite katalogą, kuriame yra visa informacija, kurią ką tik sukūrėme laikymo sutarčiai. Pavadinau savo aplanką „storage-back“ ir jame yra darbas, kurį ką tik atlikome, kad nustatytume ir įdiegtume sutartį. 

    Ekrano kadras 2020 09 01, 5 26 33 val

    Dabar mes pradėsime nuo reakcijos projekto sukūrimo, šiame pavyzdyje pavadinkime savo „saugyklos laboratorija“

    Savo terminale pradėkime savo projektą taip: 

    „npx create-reago-app“ saugykla-laboratorija

    Dabar, kai turime savo naują projekto katilą, eikime į projekto katalogą

    CD saugykla-laboratorija

    Dabar, kai esame savo projekto viduje, dabar pridėsime „Web3“ paketą, kuris leis mūsų projektui bendrauti su „Ethereum“! Daugiau apie „web3“ čia

    npm įdiegti internetą3

    „Web3“ yra vienas iš dviejų pagrindinių paketų, kuriuos galime naudoti, kitas yra „ethers.js“. Šiame pavyzdyje mes naudosime „web3“, bet jei norite sužinoti daugiau apie ethers.js, pažiūrėkite čia 

    Norėdami išsamiai paaiškinti abu, pažvelkite į šį užrašą web3 ir eteriai

    Puiku! Dabar esame beveik pasirengę, kad mūsų reakcijos projektas sąveikautų su mūsų sutartimi!

    Pirmiausia paimkime ankstesnį katalogą (man tai yra „saugojimo atgal“), kuriame yra tik darbas, kurį jau atlikome, įtraukdami mūsų išmaniąsias sutartis, o dabar pridėkime tai savo naujam reakcijos projektui. Tai gyvens tame pačiame lygyje, kaip ir mūsų src, ir dabar mes turėtume viską, ko mums reikia kartu savo reakcijos REPO viduje.

    Ekrano kadras 2020 09 01, 5 31 38 val

    Tada turėsime nustatyti failą, kuriame bus mūsų ABI informacija.

    „ABI?“

    Džiugu, kad paklausėte! 

    Sutarties taikymo dvejetainė sąsaja (ABI) yra standartinis būdas bendrauti su sutartimis „Ethereum“ ekosistemoje tiek iš blokų grandinės ribų, tiek tarp sutarties ir sutarties sąveikos. Kai ankstesniame etape sudarėme „SimpleStorage“ sutartį, ji sukūrė mums JSON failą. Patikrinkite patys, mes turime „SimpleStorage.json“ failą savo versijoje / sutartyse

    Ekrano kadras 2020 09 01, 6 04 20 val

    Pirmasis šio failo peržiūra atskleis daug informacijos, dabar turime sutelkti dėmesį tik į ABI, kad sinchronizuotume savo sutartį su mūsų sukurta priekine puse. Šiame JSON yra informacija, kurios mums reikia norint pranešti apie savo sutartį su mūsų sąsaja.

    Mūsų ABI yra objektų masyvas. Pažvelgus į failą arčiau, galite pamatyti, kad kiekvienas iš šių objektų iš tikrųjų yra kiekviena mūsų „SimpleStorage“ sutarties funkcija.

    Ekrano kadras 2020 09 01, 5 33 23 val

    Galite greitai pamatyti

    „Vardas“: „rinkinys“

    „Vardas“: „gauti“

    abi su „type:„ function “- abi funkcijas, kurias deklaravome rašydami savo išmaniąją sutartį!

    Nors triufelis užgožia kitus kelis žingsnius, mes eisime per daug „rankinį“ būdą, kaip atlikti veiksmus, kad galėtumėte susipažinti su visais pagrindais &# 128578;

    Pirmiausia eikite į priekį ir nukopijuokite abi informaciją – jos mums prireiks per akimirką. 

    Sukurkime aplanką savo src viduje pavadinimu „abi“.

    Mūsų ką tik pagaminto abi aplanko viduje dabar padarykime failą pavadinimu abi.js

    Pastaba: mums techniškai nereikia šio atskyrimo ir galime tiesiog pridėti abi.js prie savo src, tačiau abi.js failų laikymas padeda organizuoti.

    Dabar nukopijuosime abi masyvą, kurį anksčiau paėmėme iš failo „SimpleStorage.JSON“, ir pridėsime jį prie naujai sukurto abi.js failo. Šiek tiek pakeisime failą, kad mūsų projektas galėtų importuoti informaciją į mūsų „App.js“. Nepamirškite, nes tai yra a.js failas, turime pridėti eksportą, kad vėliau galėtume jį įtraukti į savo app.js. Pavadinkime sąlygą tą patį kaip ir sutartis, išskyrus su kupranugariu (žr. Kodą žemiau):

    Tai bus kodas, kurį išsaugosime faile abi.js

    eksportuoti const simpleStorage = [{konstanta: klaidinga, įvestys: [{pavadinimas: "x", tipas: "uint256", }, ], vardas: "rinkinys", išėjimai: [], mokėtini: klaidingi, būsenaKintamumas: "nemokama", tipas: "funkcija", }, {konstanta: tiesa, įvestys: [], vardas: "gauti", išėjimai: [{pavadinimas: "", tipas: "uint256", },], mokėtinas: false, stateMutability: "vaizdas", tipas: "funkcija", },];

    Laikas eiti į mūsų „App.js“ ir importuoti „Web3“ ir ką tik pagamintą abi.js failą.

    Šiame pavyzdyje taip pat ketiname naudoti kabliukus (todėl mes taip pat importuojame {useState}, galite daugiau sužinoti apie useState čia.

    Dabar „App.js“ failo viršus turėtų atrodyti taip:

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css";

    Dabar turime įsitikinti, kad visi savavališki vartotojai turi galimybę prisijungti ir naudoti mūsų „dApp“, jei tik jie turi piniginės teikėją!

    Pagrindinė piniginė, naudojama „Ethereum“ erdvėje dApp sąveikai, yra „MetaMask“, pristatyta 1 žingsnyje.

    Jei neturite „MetaMask“, apsilankykite metamask.io

    Įdiegę „MetaMask“, savo piniginę galime pasiekti naudodami:

    const web3 = naujas Web3 (Web3.givenProvider);

    „Web3.givenProvider“ bus nustatytas „Ethereum“ palaikomoje naršyklėje.

    (galite daugiau sužinoti apie tai, kodėl tai būtina čia)

    Taigi dabar mūsų kodas turėtų atrodyti taip:

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css"; const web3 = naujas Web3 (Web3.givenProvider);

    Gerai! Iki šiol mes:

    • Spindėjo „React“ projektą
    • Įdiegtas „Web3“
    • Pridėjome aplanką su „build + contract + migration“ prie mūsų „React“ projekto
    • Sukūrė abi.js failą, kuriame buvo abi duomenys, kuriuos paėmėme iš SimpleStorage.json
    • Importavo duomenis, kurių mums reikia norint sąveikauti su mūsų sutartimi
    • Sukūrė kintamąjį, leidžiantį „dApp“ bendrauti su vartotojo pinigine

    Vėlgi, nors dėl triufelių keli kiti veiksmai nereikalingi (vėliau pateiksime jums daug paprastesnę versiją), švietimo tikslais pridėsime šiek tiek daugiau rankinio „dApp“..

    Tai, ką mes padarysime dabar, bus sukurti du naujus kintamuosius: vieną išsaugoti „Ropsten“ įdiegtos sutarties adresą ir kitą, kad sutartis atitiktų mūsų ABI, kad mūsų programa žinotų, kaip su ja kalbėtis.! 

    Norėdami rasti sutarties adresą, eikite į JSON failą, kuriame buvome anksčiau (kuriame yra ABI (SimpleStorage.json)) ir slinkite į apačią. Adresas yra lauke „adresas“:

    "sudarytojas": { "vardas": "solc", "versija": "0.5.8 + įsipareigoti.23d335f2.Emscripten.clang" }, "tinklus": { "3": { "įvykius": {}, "nuorodos": {}, "adresas": "0x24164F46A62a73de326E55fe46D1239d136851d8", "actionHash": "0x1f02006b451b9e85f70acdff15a01c6520e4beddfd93a20e88a9b702a607a7b0" }}, "schemaVersion": "3.0.16", "updatedAt": "2020-06-30T20: 45: 38.686Z", "devdoc": { "metodai": {}}, "userdoc": { "metodai": {}}}

    Arba galite pereiti prie https://ropsten.etherscan.io/ ir suraskite sąskaitos, kuria dislokuota sutartis, adresą! „Etherscan“ svetainėje spustelėjus „Sutarties sukūrimas“ bus parodytas pats sutarties adresas.

    Ekrano kadras 2020 09 01, 5 43 46 val

    Dabar mes paimsime jūsų sutarties adreso kopiją ir sukursime naują kintamąjį jai išsaugoti. 

    Be to mes negalėsime bendrauti su sutartimi, o mūsų „dApp“ neveiks taip, kaip numatyta.

    Tai pridėsite prie mūsų const web3 = new Web3 (Web3.givenProvider);

    const contractAddress = "savo sutarties adresą čia";

    Tada sukursime dar vieną naują kintamąjį, pavadintą „storageContract“, kuriame bus ir mūsų sutarties adresas (kad mūsų programa žinotų, kur yra sutartis), ir ABI (taigi mūsų programa žino, kaip bendrauti su sutartimi)..

    const storageContract = new web3.eth.Contract (simpleStorage, contractAddress);

    Mūsų „App.js“ dabar turėtų atrodyti taip

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css"; const web3 = naujas Web3 (Web3.givenProvider); const contractAddress = "savo sutarties adresą čia"; const storageContract = new web3.eth.Contract (simpleStorage, contractAddress);

    Dabar turime priversti kablius laikyti kintamuosius, kurie sąveikaus su mūsų sutartimi ir sąsaja. Tai atliksime savo programos funkcijoje deklaruodami:

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css"; const web3 = naujas Web3 (Web3.givenProvider); const contractAddress = "savo sutarties adresą"; const storageContract = new web3.eth.Contract (simpleStorage, contractAddress); funkcija App () {const [skaičius, setUint] = useState (0); const [getNumber, setGet] = useState ("0");

    Pirmą kartą naudodami „useState“ (0) laikysime vartotojo nurodytą „uint256“.

    (skaičiaus, setUint, getNumber, setGaidų suteikimo taisyklės, tikimės, padės parodyti, kas vyksta)

    „useState“ („0“) reikšmė veikia kaip vietos rezervavimo priemonė, kol turėsime patvirtinimą apie pasirašytą veiksmą (mūsų deklaruotas uint256)

    „setUint“ netrukus pasikviesime grįžę (daugiau apie tai vėliau)

    Laikas mūsų logikai

    Toliau mes pridėsime savo numerio rinkinio ir numerio logiką (pridėsime numerio rinkinį savo funkcijos programoje)

    const numberSet = nesinchronizuoti (t) => {t.preventDefault (); const sąskaitos = laukti lango.ethereum.enable (); const paskyra = sąskaitos [0]; const gas = laukti storageContract.methods.set (number) .estimateGas (); const post = laukti storageContract.methods.set (number) .send ({from: account, gas,}); }; const numberGet = asinchroninis (t) => {t.preventDefault (); const post = laukti storageContract.methods.get (). call (); setGet (paštu); };

    Mes nustatėme „preventDefault“ (išsami informacija apie „PrevDefault“ rasta čia)

    Mes taip pat naudojame asinchroninį skambutį gaudami sutartį (išsami informacija apie asinchronizavimą rasta čia)

    Mūsų kablio rinkinys „getGet“ () išsaugo numatytąją vertę, kurią iš pradžių žiūrime („0“)

    const sąskaitos = laukti lango.ethereum.enable ();

    įsitikina, kad skambiname savo prijungtu adresu per „MetaMask“.

    const paskyra = sąskaitos [0];

    Traukia prisijungimo paskyrą

    Jums gali būti įdomu, kas vyksta 

    const gas = laukti storageContract.methods.set (number) .estimateGas ();

    Mūsų programai reikalingas leidimas naudotis naudotojo lėšomis, kad būtų galima sumokėti už mokesčius už dujas, bet kokias funkcijas, reikalaujančias eterio, neatsižvelgiant į tai, ar ji yra testnet, ar mainnet. Čia mūsų ryšys su „MetaMask“ yra naudingas, norint prisijungti prie šio naudojimo, kad nustatytume „uint256“ ir sumokėtume už jį (naudojant bandomąjį ETH)..

    Taigi bet kuriai funkcijai, kuriai reikia dujų, turite apskaičiuoti galimas naudojamas dujas.

    Mūsų sutarties nustatymo funkcijai reikia dujų

    „Gauti“ nėra.

    (taip yra todėl, kad „Gauti“ peržiūri tai, kas jau buvo deklaruota naudojant „Nustatyti“)

    „const post“ priims patvirtintą „uint256“, patvirtinkite operaciją (sumokėdami dujų mokestį) iš „MetaMask“ piniginės „Ropsten“ tinkle.

    Tada mes perduosime funkcijų parametrus per metodą.set () ir naudodamiesi savo deklaruotu adresu (vartotojo adresu) mes tvarkysime funkciją „Nustatyti“.

    Mes sukuriame savo sumanios sutarties operaciją perduodami savo funkcijų parametrus išmaniųjų sutarčių metodams. Set () ir numatomą dujų ir vartotojo sąskaitos adresą to.send ().

    const post = laukti storageContract.methods.set (number) .send ({from: account, gas,});

    Tai turėtų būti visa logika, kurią turime aprėpti savo numerio rinkinyje.

    Dabar mums reikia mūsų numerioGet

    const numberGet = asinchroninis (t) => {t.preventDefault (); const post = laukti storageContract.methods.get (). call (); setGet (paštu); };

    Mūsų „const post“ gauna mūsų nustatytą numerį ir „setGet“ perduoda naują deklaruotą vertę

    Taigi mūsų „0“ bus „ClickClick“ nurodys mūsų numerį „Get“ ir padarys mūsų nepažįstamą256!

     Taigi dabar jūsų app.js turėtų atrodyti taip

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css"; const web3 = naujas Web3 (Web3.givenProvider); const contractAddress = "savo sutarties adresą"; const storageContract = new web3.eth.Contract (simpleStorage, contractAddress); funkcija App () {const [skaičius, setUint] = useState (0); const [getNumber, setGet] = useState ("0"); const numberSet = nesinchronizuoti (t) => {t.preventDefault (); const sąskaitos = laukti lango.ethereum.enable (); const paskyra = sąskaitos [0]; const gas = laukti storageContract.methods.set (number) .estimateGas (); const post = laukti storageContract.methods.set (number) .send ({from: account, gas,}); }; const numberGet = asinchroninis (t) => {t.preventDefault (); const post = laukti storageContract.methods.get (). call (); setGet (paštu); };

    Sukurkime labai paprastą atvaizdavimo grąžą, kad galėtume išbandyti, ar galime 

    • nustatykite unint256 vertę,
    • Patraukite mūsų metamask piniginę ir patvirtinkite operaciją
    • Mokėkite dujų kainą
    • tada gaukite vertę (unint256), kurią išsaugojome, kai baigsis operacija.

    Mūsų grąža atrodo taip: 

    grįžti (nustatykite uint256: setUint (t.target.value)} /> Patvirtinti

    Gaukite savo uint256 {getNumber}); } eksportuoti numatytąją programą;

    Keli greiti CSS

    Dabar pereikime prie failo „App.css“, ištrinkite katilo plokštės kodą ir vietoj to pridėkite

    .main {text-align: centre; ekranas: lankstus; pateisinti-turinys: centras; fono spalva: # f2f1f5; aukštis: 100vh; } .card {min-height: 50vh; plotis: 50vw; ekranas: lankstus; lankstumo kryptis: kolona; lygiuoti daiktai: centras; pateisinti-turinys: centras; } .forma {aukštis: 20vh; plotis: 20vw; ekranas: lankstus; pateisinti turinį: erdvę tolygiai; lankstumo kryptis: kolona; }. mygtukas {plotis: 20vw; aukštis: 5vh; }

    Dabar mes esame pasirengę išbandyti!

    Savo terminalo paleisti

    verpalų pradžia

    Mūsų vietiniame šeimininke: 3000 turėtume atrodyti taip

     

    Ekrano kadras 2020 09 01, 6 12 49 val

    Dabar turėtume galėti įvesti unint256 reikšmę įvesties lauke!

    Patvirtinę savo numerį „dApp“, pasirašome per „MetaMask“ (įsitikinkite, kad jūsų piniginė nustatyta į „Ropsten“ tinklą)

    confrim1

    Mes tai padarėme! &# 129303;

    Dabar mes turime savo išmaniąją sutartį, susijusią su sąsaja ir galimybe manipuliuoti funkcija „Nustatyti“ (su sąlyga, kad turėsime bandomąją ETH, kad sumokėtume dujų mokestį už sandorį). Tada mes galime pasikviesti funkciją Gauti ir nuskaityti išsaugotą uint265 vertę.

    Gana šaunu!?!

    Papildomas stilius 

    Dabar atėjo laikas parodyti, kaip lengvai gali būti dar populiaresnė „Web2“ technologija įdiegta į mūsų projektą.

    Mes naudosime MUI norėdami pridėti pagrindinį stilių, jei jau kuriate „React“, galite būti susipažinę su „material-ui“. (Išsami informacija rasta čia) Trumpai tariant, „Material-UI“ arba „MUI“ yra labai populiari „React“ sistema, leidžianti greitai paspartinti projektą su daugybe paruoštų stilių, jei laikysitės pavadinimo tvarkos. Taip pat labai lengva manipuliuoti, jei norite tiesiog naudoti pagrindą ir pritaikyti iš ten.

    * Tai bus labai trumpas pavyzdys, kaip pridėti MUI prie projekto su nedideliais papildymais, kad pademonstruotumėte, kaip greitai galite įtraukti mūsų projektą, esantį „Web2“ technologijoje. 

    Pridedama MUI

    Pradėsime paleisdami komandą (kol dar esame savo projekto kataloge terminale (jei jūsų programa vis dar veikia, turėsite ją uždaryti („Ctrl“ + c) arba atidaryti naują skirtuką)):

    Norėdami įdiegti naudodami „npm“:

    npm install @ material-ui / core

    Arba su verpalais:

    siūlai pridėti @ material-ui / core

    Dabar, kai suleisime MUI, pradėsime nuo stiliaus pakeitimo. Failo „app.js“ viršuje mes importuosime keletą naujų dalykų:

    importuoti {simpleStorage} iš "./ abi / abi"; importuoti mygtuką iš "@ material-ui / core / mygtukas"; importuoti „TextField“ iš "@ material-ui / core / TextField"; importuoti „{makeStyles}“ iš "@ material-ui / core / styles";

    Importavus „{makeStyles}“, galime manipuliuoti mygtukų ir teksto lauko (šiuo atveju) stiliumi, taip pat importuoti numatytąjį MUI stilių.. 

    Dabar mes sukursime kintamąjį (virš mūsų funkcijos), kuris suteiks MUI katilinės stilių

    const useStyles = makeStyles ((tema) => ({root: { "& > *": {paraštė: tema. tarpai (1),},},}));

    Dabar savo „App“ funkcijoje mes taip pat pridėsime kintamąjį, pavadintą „klases“, traukdami apibrėžtus stilius, kuriuos ką tik deklaravome aukščiau.

    funkcija App () {const class = useStyles (); const [skaičius, setUint] = useState (0); const [getNumber, setGet] = useState ("0");

    Grąžindami dabar atliksime pakeitimus, kad kai kuriuos laukus pakeistume ką tik importuotais.

    return (setUint (t.target.value)} variantas ="nurodyta" /> Patvirtinti

    Gaukite savo uint256 {getNumber}); } eksportuoti numatytąją programą;

    Dabar jūsų kodas turėtų atrodyti taip

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importas "./App.css"; importuoti „{makeStyles}“ iš "@ material-ui / core / styles"; importuoti mygtuką iš "@ material-ui / core / mygtukas"; importuoti „TextField“ iš "@ material-ui / core / TextField"; const useStyles = makeStyles ((tema) => ({root: { "& > *": {paraštė: tema. tarpai (1),},},})); const web3 = naujas Web3 (Web3.givenProvider); const contractAddress = "savo sutarties adresą čia"; const storageContract = new web3.eth.Contract (simpleStorage, contractAddress); funkcija App () {const class = useStyles (); const [skaičius, setUint] = useState (0); const [getNumber, setGet] = useState ("0"); const numberSet = nesinchronizuoti (t) => {t.preventDefault (); const sąskaitos = laukti lango.ethereum.enable (); const paskyra = sąskaitos [0]; const gas = laukti storageContract.methods.set (number) .estimateGas (); const post = laukti storageContract.methods.set (number) .send ({from: account, gas,}); }; const numberGet = asinchroninis (t) => {t.preventDefault (); const post = laukti storageContract.methods.get (). call (); setGet (paštu); }; return (setUint (t.target.value)} variantas ="nurodyta" /> Patvirtinti

    Gaukite savo uint256 {getNumber}); } eksportuoti numatytąją programą;

    Dabar, jei pažvelgsime į mūsų reakcijos projektą, jis turėtų atrodyti taip!

    Ekrano kadras 2020 09 01, 6 48 55 val

    Šauniai padirbėta!

    Mes vis dar turime visas ankstesnes funkcijas ir dabar įdiegėme lengvai naudojamą sistemą, kad galėtume toliau pritaikyti savo projektą, kad ir kaip norėtume. Pažvelkite į MUI dokumentacija eksperimentuoti su savo papildymais / modifikacijomis!

    Premijų turas 

    Būtų malonu parodyti vartotojams prisijungimo adresą per mūsų „dApp“, ar ne?

    Na, padarysime labai greitą ir pagrindinį komponentą, kad tai padarytume!

    Pirmiausia sukursime atskirą komponentą, kurį galėsime importuoti atgal į „App.js“ failą. Patartina atskirti savo logiką, kad ne tik būtų lengva naršyti „App.js“, bet ir laikytis komponento praktikos, idealiu atveju atliekant tik vieną dalyką. Jei jis galų gale auga, jis turėtų būti suskaidytas į mažesnius komponentus.

    Komponento statyba 

    Sukursime naują aplanką, vadinamą komponentais, tame pačiame lygyje, kaip ir mūsų src, ir tame aplanke padarysime failą „Nav.js“. Mūsų projekto pastoliai dabar turėtų atrodyti maždaug taip

    Ekrano kadras 2020 09 01, 6 47 07 val

    Komponentų aplanke taip pat sukursime failą „Nav.css“, kad galėtume importuoti bet kokius stilius, kuriuos pritaikome specialiai „Nav“ komponentui.

    Atidarykime „Nav.js“ ir importuokime „React“, „Web3“ ir failą „empty.css“

    importuoti reaguoti iš "reaguoti"; importuoti „Web3“ iš "3"; importas "./Nav.css"

    Dabar sukursime „Nav“ klasę ir joje pridėsime tik keletą dalykų, kad būtų parodytas mūsų susietas adresas. Pirmiausia nustatysime savo valstybę skaityti sąskaitą

    „Class Nav“ pratęsia „React.Component“ {state = {account: "" };

    Mes vis dar priklausysime savo klasei, įkeldami sąskaitą, iš kurios galėsite skaityti, pridėdami savo asinchroninę loadAccount logiką

    async loadAccount () {const web3 = new Web3 (Web3.givenProvider || "http: // localhost: 8080"); const tinklas = laukti web3.eth.net.getNetworkType (); const sąskaitos = laukia web3.eth.getAccounts (); this.setState ({account: accounts [0]}); }

    Toliau sukursime „komponentDidMount“ (kuris bus iškviestas iškart po to, kai komponentas bus sumontuotas). Mūsų atveju bus įkelta įkelta paskyra. Skaityti daugiau čia

    componentDidMount () {this.loadAccount (); }

    Paraštės užrašas:

    Tai gali būti padaryta kitaip, vietoj klasės galime sukurti funkciją ir naudoti kablius, priešingus komponentuiDidMount, tačiau dėl šio pavyzdžio mes laikysimės šio metodo.

    Tada mes sukursime pateikimą virš mūsų grąžos, „render“ yra metodas, kurio reikia, kai rašote „React“ komponentą naudodami klasės metodą. Grąžindami pridedame adresų klasę prie savo div (kad vėliau būtų suteikta pagrindinė stilistika) kartu su žyma p, kad būtų parodytas prijungtas adresas, kurį mes gavome naudodami {this.state.account}

    render () {return (Jūsų susietas adresas: {this.state.account}); }} eksportuoti numatytąjį „Nav“;

    Mūsų failas „Nav.js“ dabar turėtų atrodyti taip

    importuoti reaguoti iš "reaguoti"; importuoti „Web3“ iš "3"; importas "./Nav.css" „Class Nav“ pratęsia „React.Component“ {state = {account: "" }; async loadAccount () {const web3 = new Web3 (Web3.givenProvider || "http: // localhost: 8080"); const tinklas = laukti web3.eth.net.getNetworkType (); const sąskaitos = laukia web3.eth.getAccounts (); this.setState ({account: accounts [0]}); } componentDidMount () {this.loadAccount (); } render () {return (Jūsų susietas adresas: {this.state.account}); }} eksportuoti numatytąjį „Nav“;

     

    Eikime į „Nav.css“ failą ir pridėkime labai paprastą stilių

    .adresas {display: flex; pateisinti-turinys: centras; }

    Techniškai galėtumėte tai pridėti prie failo „App.css“, nepamirškite, nors gana greitai, tai gali būti netvarkinga. Komponentai turėtų būti pakartotinai naudojami ir, norint išvengti kuo didesnės trinties, suskirstydami savo darbą į darbą, galite sutaupyti galvos skausmo kelyje.

    Dabar grįžkime į savo „App.js“ ir importuokite naujai pagamintą komponentą ir įsitikinkite, kad jį pridėjome prie savo grąžinimo, kad galėtume jį parodyti!

    Mūsų baigtas „App.js“ failas turėtų atrodyti taip

    importuoti „React“, {useState} iš "reaguoti"; importuoti {simpleStorage} iš "./ abi / abi"; importuoti „Web3“ iš "3"; importuoti „Nav“ iš "./components/Nav.js"; importas "./App.css"; importuoti „{makeStyles}“ iš "@ material-ui / core / styles"; importuoti mygtuką iš "@ material-ui / core / mygtukas"; importuoti „TextField“ iš "@ material-ui / core / TextField"; const useStyles = makeStyles ((tema) => ({root: { "& > *": {paraštė: tema. tarpai (1),},},})); const web3 = naujas Web3 (Web3.givenProvider); const contractAddress = "čia jūsų adresas"; const storageContract = new web3.eth.Contract (simpleStorage, contractAddress); funkcija App () {const class = useStyles (); const [skaičius, setUint] = useState (0); const [getNumber, setGet] = useState ("0"); const numberSet = nesinchronizuoti (t) => {t.preventDefault (); const sąskaitos = laukti lango.ethereum.enable (); const paskyra = sąskaitos [0]; const gas = laukti storageContract.methods.set (number) .estimateGas (); const post = laukti storageContract.methods.set (number) .send ({from: account, gas,}); }; const numberGet = asinchroninis (t) => {t.preventDefault (); const post = laukti storageContract.methods.get (). call (); setGet (paštu); }; grįžti ( setUint (t.target.value)} variantas ="nurodyta" /> Patvirtinti

    Gaukite savo uint256 {getNumber}); } eksportuoti numatytąją programą;

    Dabar turėtume pamatyti savo prijungtą adresą viršuje ir vis tiek išsaugoti visas savo funkcijas!

    premija V1

    &# 127881; Mes tai padarėme! &# 127881;

    Dabar turime „dApp“, kurį sukūrėme iš pagrindų. Mes įtraukėme savo išmaniąją sutartį į „React“ projektą, parašėme logiką, kad įsitikintume, jog turime vartotojo funkcionalumą, sukūrėme komponentą, kuris atkuria prijungtą adresą, ir netgi pridėjome populiarią stiliaus struktūrą prie savo projekto.

    Šauniai padirbėta! Tai tik jūsų „Web3“ kūrimo nuotykių pradžia, ir jūs jau turite ką parodyti, kad ne tik sukūrėte, bet ir apsivyniojote. Kreipkitės į mus nesantaikoje ir pasidalykite su mumis savo projektu (ypač jei atlikote kokių nors pakeitimų ar papildymų)!

      Kūrėjų įtraukimas: 1 žingsnisKūrėjų įtraukimas 1 žingsnis

      Kūrėjų įtraukimas: 1 žingsnis

      Kūrėjų įtraukimas: 2 žingsnisKūrėjų įtraukimas į 2 žingsnį

      Kūrėjų įtraukimas: 2 žingsnis

      10 minučių orientacija į Ethereum10 minučių orientacija į Ethereum

      10 minučių orientacija į Ethereum

    Mike Owergreen Administrator
    Sorry! The Author has not filled his profile.
    follow me