Web Uygulamalarında Akıllı Kontratlarla Etkileşim

Mustafa Mert KISA
lTunes Tribe
Published in
5 min readSep 14, 2021

Merhabalar, son dönemlerimizin popüler teknolojilerinden bir tanesi olan blokzinciri teknolojisinin mutfağına girmeye ne dersiniz? Buna birlikte bir merkeziyetsiz uygulama hazırlayarak başlayabiliriz.

Akıllı Kontrat

Akıllı kontrat veya sözleşme dediğimiz kavram sizlere yabancı geliyor mu? İsterseniz biraz bu kavram üzerinde durarak anlamaya çalışalım.

İçecek otomatları akıllı sözleşmeleri anlamamız açısından çok güzel bir örnektir. Otomatlar ile makineye istediğimiz ürünü belirtiriz ve ücreti ödeyerek talebimizin gerçekleştiğini gözlemleriz. Burada ki faaliyetimizde marketler, bakkallar veya büfeler gibi üçüncü şahıslar yer almamaktadır. Akıllı sözleşmeleri bu açıdan ele alabiliriz. Blokzinciri teknolojisinin kazandırmış olduğu akıllı kontrat yapısı ile mevcut iş planlarımızı p2p yapıya getirebilir, aracı kurumları ortadan kaldırarak maliyetleri azaltabilir ve dağıtık mimarinin sunmuş olduğu güvenilirlikten faydalanabiliriz. Kısacası yaşadığımız çağda her şey programlanabilir yani “akıllı” olarak tabir ettiğimiz hâle gelirken, hayatımızın her anının parçası olan sözleşmelerimiz neden çağın gerisinde kalsınlar ki…

Bizlerde şimdi web uygulamamız için basit bir akıllı kontrat hazırlayacağız. Solidity dilini kullanacağımız akıllı sözleşmemizin geliştirme ve dağıtım süreçlerini https://remix.ethereum.org/ adresi üzerinden gerçekleştirebiliriz.

pragma solidity ^0.6.6;contract MyNumberContract {
uint public myNumber = 28;

function setMyNumber(uint _myNumber) public {
myNumber = _myNumber;
}
}

MyNumberContract içerisine public olarak yani kullanıcıların erişebildikleri myNumber değişkenimizi tanımlıyoruz ve 28 olarak sayımızı belirliyoruz. Daha sonra setMyNumber fonksiyonundan yararlanarak myNumber değerimizin karesini alıp blokzincirine göndermeyi hedefliyoruz.

Arayüz & Kontrat Bağlantıları

Kontratımızı hazırladıktan sonra şimdi projemize dönebiliriz. Arayüz için React kullanabiliriz. Bir proje dosyası oluşturup ardından içerisine web3.js modülünü kurarak kodlama aşamasına geçebiliriz.

npm install web3

Modülümüzü app.js dosyamıza dahil edip bağlantıları kurmaya başlayalım. Öncelikle uygulama başlatıldığında Metamask ile kullanıcının cüzdan adresine erişmek istiyoruz. Ardından state olarak ekliyoruz ve HTML çıktısı içerisinde render ederek adresimizi gözlemliyoruz.

import React, { Component } from 'react'import Web3 from 'web3';import './App.css'class App extends Component {componentWillMount() {this.loadBlockchainComponent()}async loadBlockchainComponent() {if (window.ethereum) {window.web3 = new Web3(window.ethereum);window.ethereum.enable();}const web3 = new Web3(Web3.givenProvider)const accounts = await web3.eth.getAccounts()this.setState({ account: accounts[0] })}
constructor(props) {super(props)this.state = { account: '' }}render() {return (<div className="container"><h1>Merhaba My Number dApp!</h1><p>Cüzdan adresi: {this.state.account}</p></div>);}}export default App;

Çıktımız şu şekilde oluyor:

Uygulamamızın Metamask bağlantılarını gerçekleştirdik. Artık Remix ile sözleşmemizi bir blokzincirine dağıtıp uygulamamıza entegre etme vaktimiz geldi. Bunun için sözleşmemizi önce derleyeceğiz ve ABI (Application Binary Interface) dosyamızı alacağız, ardından deploy ederek kontrat adresimize ulaşacağız.

Compiler ederek ABI’yi alıyoruz.
Deploy ederek kontrat adresimizi kopyalıyoruz. Ben Ethereum Rinkeby Test Ağını tercih ettim.

Bu iki bilgiyi saklamak için build klasörü oluşturuyoruz ve içerisine contract-info.js dosyası ekleyerek ABI ve adres bilgilerini burada tutuyoruz. Kontrat yapılandırmamızı da yapıp diğer dosyalarda kullanmak üzere export edeceğiz.

import Web3 from 'web3';const web3 = new Web3(Web3.givenProvider)const contractAddress = "0x4A8e405E9472d4fbc3FE8589858e89e26932AF8C"const contractABI = [{"inputs": [],"name": "myNumber","outputs": [{"internalType": "uint256","name": "","type": "uint256"}],"stateMutability": "view","type": "function"},{"inputs": [{"internalType": "uint256","name": "_myNumber","type": "uint256"}],"name": "setMyNumber","outputs": [],"stateMutability": "nonpayable","type": "function"}]export const myContract = new web3.eth.Contract(contractABI, contractAddress)

Böylelikle src/app.js dosyamızda myContract metoduna ihtiyacımız olacak. Metodumuzu import ederek kontrat ile iletişim kurmaya çalışacağız.

import { myContract } from './build/contract-info'

HTML bölümümüzde render edeceğimiz çıktılarımızı düzenleyip gereken stateleri ekliyoruz. İki buton kullanacağız. btnCall ile blokzincirinde mevcutta yer alan sayımızı çağıracağız ve Sayımız: bölümünde ekrana bastırmaya çalışacağız. Diğer butonumuz olan btnUpdate ile de sayımızın karesini alarak tekrar blokzincirine gönderme sürecini tetiklemeyi amaçlıyoruz.

render() {return (<div className="container"><h1>Merhaba My Number dApp!</h1><p>Cüzdan adresi: {this.state.account}</p><hr /><h2>Sayımız: {this.state.num}</h2><br /><button id="btnCall" onClick={() => this.callMyNumber()}>Çağır</button><br /><br /><button id="btnUpdate" onClick={() => this.setMyNumber()}>Karesini Hesapla</button></div>);}

Arayüzümüzde uygulamamızı çalıştırmak için gereken bileşenleri ekledik. Şimdi bunları ilgili fonksiyonları ile birleştirerek uygulamamızı tamamlayalım.

callMyNumber fonksiyonu oluşturuyoruz ve içerisinde kontratımızda yer alan myNumber ile etkileşim kuruyoruz. Şuan sadece myNumber’ı gözlemlemek istiyoruz bu yüzden call() fonksiyonunu tercih etmeliyiz. Akıllı sözleşmemizden elde ettiğimiz değerimizi setState() fonksiyonu ile num state’imize tanımlıyoruz.

setMyNumber fonksiyonunda ise ilk olarak kare alma işlemini hallediyoruz. Sonrasında tekrar kontratımız ile iletişime geçiyoruz fakat bu sefer kontrata bir veri göndermek istediğim için send() fonksiyonunu tercih ediyorum. Bu fonksiyonu çalıştırdığımızda Metamask bizden fee kesmek isteyecektir. Bu işlemi konsola yazdırarak transaction’ımızın detaylı bilgilerini inceleyebiliriz. Son olarak num state’ini newNumber ile güncelleyerek değişiklikleri arayüzümüz üzerinden de takip etmeyi istiyoruz.

async callMyNumber() {const myNumber = await myContract.methods.myNumber().call()this.setState({ num: myNumber })}
async setMyNumber() {const newNumber = parseInt(this.state.num) * parseInt(this.state.num)const setNewNumber = await myContract.methods.setMyNumber(newNumber).send({ from: this.state.account });console.log(setNewNumber)this.setState({ num: newNumber })}

Harika! Artık uygulamamız hedeflediğimiz tüm işlevleri yerine getirebilir. Kazanımlarımızın üstünden geçmek gerekirse:

  • Solidity ile uygulamamız için gereken fonksiyonları içeren bir akıllı kontrat hazırladık.
  • Akıllı kontratımızı derledik ve tercih ettiğimiz blokzinciri ağına dağıttık, ardından arayüz tarafında ihtiyacımız olan kontrat adresimize ve ABI dosyamıza ulaştık.
  • Bir React projesi oluşturduk ve Web3.js kütüphanesinden yararlanarak Metamask cüzdanımızı ve akıllı kontratımızı hazırladığımız arayüze entegre ettik.
  • Akıllı kontratımızda belirttiğimiz gibi blokzinciri üzerinde bir sayı tutuyorduk ve setMyNumber fonksiyonu ile istersek bu sayıyı güncelleyebiliyorduk. Bizlerde önce sayımızı blokzincirinden çağırıyoruz daha sonrasında karesini alarak tekrar iletiyoruz ve blokzincirinde yer alan sayımızı güncellemiş oluyoruz. Göreceksiniz ki her karesini alıp ilettiğinizde sonrasında tekrar çağırmak istediğinizde ilk eklediğiniz sayı değil, en son işlem yaptığınız sayı sizleri karşılayacak.

Ben birkaç kez sayı tanımladım ve karelerini aldım. Son işlemimin transaction hash kodunu aşağıya ekledim. Aynı zamanda çalışmanın dosyalarının olduğu github linki de aşağıda sizleri bekliyor. Şimdiden kolay gelsin, iyi çalışmalar :)

0xe0475a0fd57c171a6ac441bfef6f2efc5c26a45247c350854abf3e1c16909cdc

--

--