fbpixel

Deploy inteligentnego kontraktu z kodu część 1

Cześć,
Dzisiaj pokażę jak przeprowadzić deployment swojego inteligentnego kontraktu (smart contract) na blockchain Ethereum z poziomu kodu 🙂

W ostatnim wpisie pokazałem jak podłączyć się do testowej sieci rinkeby. Jeżeli ominął Cię ten wpis (Czyli nie zapisałeś się na newsletter 🙁 ) to możesz go przeczytać tutaj.

Async/ await i promisy

Żeby wogóle myśleć o pisaniu kodu do komunikacji z blockchain i Dapps-ami (Decentralized applications). Przydałoby się znać przynajmniej podstawowy korzystania z asynchronicznych wywołań.

Wymyśliłem sobie metodę get, która to pobiera ostatnią transakcję z blockchain jej implementacja nie ma znaczenia. Zacznijmy od tego jak to było realizowane w przeszłości na podstawie języka javascript. Służyły do tego tak zwane callbacki:

function onSuccess(result) {
   ...
}
function onFailure(error) {
   ...
}

get('http://eth.com/transactions/last', onSuccess, onFailure);

Kod powyżej nie wygląda źle prawda? Ale co gdyby w razie powodzenia wywołania metody get trzeba było zrobić coś jeszcze? Zoabczmy jak mógłby wyglądać taki kod:

function onSuccess(result) {
   get(result['prev',]['url'], onSuccess2, onFailure2);
}
function onFailure(error) {
   ...
}

get('http://eth.com/transactions/last', onSuccess, onFailure);

Jak widzicie trzeba dodać kolejne dwie metody do obsługi wyniku co nie wygląda już tak fajnie. Przez niektórych nazywane było „callback hell”, czyli w dosłownym tłumaczeniu piekłem callback-ów. Zobaczmy jak mógłby wyglądać ten sam kod zapisany z użyciem tzw. promise-ów, czyli bardziej nowoczesnej metody konsumowania zapytań asynchronicznych. Oczywiście implementacja metody get musiałaby być dostosowana i powinna zwracać promise przyjmując jednocześnie tylko jeden argument:

var promise = get('http://eth.com/transactions/last');
promise.then( function (result) {
   get(result['prev',]['url']).then( function (data) {
      ...
   }).catch(function (error) {
      ...
   });
}).catch(function (error) {
   ...
});

W zasadzie promise sprowadza się do tego, że może on nie dostać odpowiedzi od razu, ale jeżeli przekażemy callback (then z funkcją lub w przypadku błędu catch z funkcją) to poinformuje on (promise) kiedy tylko otrzyma odpowiedź. Oprócz tego nie ma opcji, żeby dostać odpowiedź bezpośrednio bez użycia funkcji callback. W przypadku kiedy wszystko pójdzie bez problemów zostanie wywołana funkcja przekazana do metody then. W przypadku błędu zostanie wywołana funkcja przekazana do metody catch.

Jeżeli ktoś jednak zna najbardziej współczesne standardy w javascript to zapewne powie, promise’y to przeżytek użyj async await. No to zobaczmy jak powyższy przykład mógłby wyglądać z użyciem async i await

async function getPrev() { 
   try {   
      var last = await get('http://eth.com/transactions/last');
      var prev = await get(result['prev',]['url']);
      return prev;
   } catch (error) {
   }
}

var promise = getPrev();

Await powoduje zwrócenie wartości z promise’a. Ważne żeby funkcja miała async inaczej wywołanie await nie będzie możliwe. Przy wywołaniu javascript zamieni to automatycznie na odpowiedni kod korzystający promise’ów. Należy również pamiętać, że wywołanie funkcji oznaczonej jako asynchroniczna nie zwraca wartości, a kolejnego promise’a. Skoro mamy to już wyjaśnione to możemy przejść dalej.

Biblioteki API

Pisanie zapytań do klienta ethereum manualnie byłoby ciekawą, lecz upierdliwą czynnością, dlatego znacznie lepszą opcją jest użycie gotowej biblioteki. Oto część dostępnych bibliotek w zależności od języka którego chcielibyście użyć:

  • web3.js – javascript i z tego skorzystamy w tym wpisie
  • web3j – java
  • web3.py – python
  • Nethereum – .net

Podstawowe operacje z klientem

Zobaczmy jak stworzyć obiekt łączący się do klienta ethereum (geth):

let Web3 = require('web3');
let web3 = new Web3();
web3.setProvider(
   new web3.providers.HttpProvider(
      'http://localhost:8545'
));

Najpierw wczytujemy bibliotekę używając require. Następnie tworzymy instancję Web3. W kolejnym kroku określamy do jakiego adresu klienta sieci ethereum chcemy się podłączyć (do celów testowych będzie to zawsze lokalny adres na porcie 8545).
Zobaczmy teraz jak połączyć się z istniejącym inteligentnym kontraktem używając web3.js:

var jsonInterface = readJsonFile("abi.json");
var contractAddress ="0x...";
var contract = new web3.eth.Contract(
   jsonInterface,
   contractAddress
);

Do podłączenia potrzebujemy jsona, który opisuje dostepne metody danego kontraktu (skąd go wziąć do tego wrócimy) i oczywiście adres kontraktu. Zobaczmy jak podłączyć się do kontraktu z przykładu z pierwszego wpisu.

 var senderAddress = "0x...";
var resultPromise = await contract.methods.send("Hi") // to stworzy  transakcję, ale nic oprócz tego. Musimy ją jeszcze wysłać do sieci Ethereum
   .send({
      from: senderAddress,
      gas: 1500000 // opcjonalne, ale zalecane
   });

// możemy również użyć metody zwracającej dane
var promise = await contract.methods.get()
   .call()

To jeszcze na koniec tak wygląda kod, potrzebny do deploymentu nowego kontraktu:

 var HelloContract = new web3.net.Contract(jsonInterface);
var newContract = await HelloContract
   .deploy({data: bytecode})
   .send({
      from: senderAddress,
      gas: 1500000
   });

Ważne jest użycie odpowiedniej przestrzeni nazw z biblioteki web3. Dla stworzonych kontraktów ta przestrzeń to eth a dla nowych net. Oprócz tego pojawiła się zagadkowa zmienna bytecode. Jest to kod, wygenerowany przy budowaniu kodu solidity.
Następny post rozpocznę z tego miejsca, czyli od zbudowania i zdeployowania nowego inteligentnego kontraktu do testowej sieci Ethereum.

Do następnego!
Łukasz