REMIX IDE, Solidity ve Openzeppelin Kullanarak Kontrat Geliştirme, Arz Çeşitleri ve Erişim Kontrolü

Eray USTA
9 min readFeb 9, 2022

Merhaba, Ethereum ile ilgili 2021 raporunu paylaşarak medium a yazmaya başlamıştım. Bugün ise şu yazıdan faydalanarak tam klavuzu çevirmeye çalışacağım. Yazıyı bitirdiğinizde kendi para biriminizi oluşturmuş ve testnet e dağıtmış olacaksınız. Yazıyı okurken tavsiyem ekranınızı ikiye bölüp yazı esnasında adımları okurken benimle birlikte uygulamanızdır.

Eğitim içerisinde Solidity ve Openzeppelin tarafından sağlanan ERC20 token standardı kullanarak bir kripto para biriminin nasıl geliştirileceğini öğreneceksiniz.

İlk olarak REMIX IDE’yi kullanarak Akıllı Sözleşme yazacağız. Ardından , bu Akıllı Sözleşmeyi Polygon Mumbai Testnetinde dağıtmak için Hardhat’ı kullanarak yerel bir ortam kuracağız.

Sonunda ise şu konulara hakim olacaksınız;

  • REMIX IDE’yi kullanarak Ethereum Akıllı Sözleşme geliştirme ve dağıtma.
  • Solidity ile ERC20 Token oluşturma.
  • Hardhat kullanarak yerel(local) Ethereum geliştirme ortamı kurma.
  • Token’ı Polygon Mumbai’de dağıtma.
  • Kendi tokenınızı MetaMask üzerinde görme.

Hadi şimdi doğrudan ilk kripto para biriminizi nasıl geliştireceğinize başlayalım.

REMIX ile Ortamı Ayarlayın

Kripto Para Birimi için Akıllı Sözleşmemizi REMIX IDE kullanarak geliştireceğiz.

remix.ethereum.org adresine gidin, contracts klasörünü açın ve “Token.sol” adlı yeni bir dosya oluşturun :

Yeni bir Solidity dosyası oluşturulduğunda, ilk satırda lisans tanımlayıcısını ve pragmayı eklemek zorunludur.

Token.sol dosyasına aşağıdaki kodu yazın:

// SPDX-License-Identifier: GPL-3.0  
pragma solidity ^0.8.0;

Pragma solidity satırında “^” işareti sonrası gelen 0.8.0 bilgisi hangi derleyici ile uyumlu olduğu anlamına gelir.

Şimdi ERC20 sözleşmesini OpenZeppelin’den içe aktarmamız gerekiyor , ancak önce kısaca ERC20 Token’ın ne olduğundan ve neden önemli olduğundan bahsedeyim.

Bu kavrama zaten aşina iseniz, bir sonraki paragrafa geçebilirsiniz.

ERC20 nedir? — Bir Kripto Para Birimi Nasıl Geliştirilir

Resmi OpenZeppelin belgelerine göre:

“Bir ERC20 token kontratı, değiştirilebilir tokenların (fungible tokens) kaydını tutar : herhangi bir token, diğer herhangi bir tokenla tam olarak eşittir; hiçbir tokenın kendileriyle ilişkili özel hakları veya davranışları yoktur. Bu, ERC20 tokenlarını bir değişim aracı para birimi, oy verme, stake etme ve daha fazlası gibi şeyler için kullanışlı hale getiriyor.”

Basitçe söylemek gerekirse, ERC20, methodları ve üyeleriyle, diğer kullanım durumlarında da uygulamalar bulduğu için daha geniş bir anlamla, kripto para birimleri dediğimiz şeyin mantığını çalıştıran bir sınıftan başka bir şey değildir.

OpenZeppelin ise ERC sözleşme sınıflarını koruyan standart kütüphane olarak kabul edilir.

ERC20 token standardı hakkında daha fazla bilgi edinmek istiyorsanız, işte birkaç kaynak:

Artık OpenZeppelin kitaplığını neden içe aktardığımızı ve ERC20'nin ne anlama geldiğini kısaca anladığımıza göre, koda geri dönelim.

Token Akıllı Sözleşmesini Oluşturun

OpenZeppelin ERC20 sözleşmesini Token.sol dosyasından çağırın. Bu satır ERC20 standartını kendi sözleşmemiz içerisine dahil edecektir.:

// SPDX-License-Identifier: GPL-3.0pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Ve ERC20.sol sözleşmesinden miras (inherit) alarak Token’ı başlatın:

// SPDX-License-Identifier: GPL-3.0pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract NilToken is ERC20{ 
}

Burada, ERC20 OpenZeppelin’in sözleşmesinden “is” anahtar sözcüğü ile miras alarak “NilToken” adlı yeni bir sözleşme ilan ediyoruz .

ERC20 sözleşmesinden devralma, kontrat içerisindeki _mint() ve balanceOf() yöntemlerine erişim sağlayacaktır . Mevcut tüm yöntemlere göz atmak isterseniz, resmi ERC20 belgelerine göz atabilirsiniz.

Bir sonraki adım, sözleşmenin kurucusunu(constructor) çağırmak ve Token’ın adını ve sembolünü belirlemektir. Bunu yapmak için, sözleşmenizi aşağıdaki şekilde yazın:

// SPDX-License-Identifier: GPL-3.0pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract DevToken is ERC20{     
constructor() ERC20("DevToken", "DVT"){
}
}

Harika! 🎉 Token test etmeden ve daha karmaşık konulara geçmeden önce son bir adım, şimdi kripto para birimimizi etkin bir şekilde yayınlamamız gerekiyor.

Kripto Parayı Basmak (Mint Etmek)

ERC20 sözleşmesinden miras (inherit) almak, bize yeni tokenlar oluşturmak için kullanılan _mint() yöntemine erişim sağlar ve tam olarak şimdi ihtiyacımız olan şeyi belirli bir adrese gönderir.

Mint etmek, bilgiyi doğrulama , yeni bir blok oluşturma ve bu bilgiyi blok zincirine kaydetme süreci olarak tanımlanır . Basitçe söylemek gerekirse, “mint” şu anlama gelir: bir dizi Token veya bir NFT gibi bir şey yaratmak ve onu blok zincirine kaydetmek.

Diyelim ki 1000 token oluşturup cüzdanımıza göndermek istiyoruz, bunu başarmak için “constructor” a aşağıdaki kodu ekleyebiliriz:

contract DevToken is ERC20{
constructor() ERC20("DevToken", "DVT"){
_mint(msg.sender,1000*10**18); }
}

Burada aslında çok şey oluyor, hadi anlamak için bir saniye ayıralım:

Her şeyden önce, tokenları yayınlamaktan sorumlu olan ve iki parametre isteyen _mint() işlevini çağırıyoruz:

// openzeppelin dökümanından
_mint(address account, uint256 amount)
  • account : tokenları alacak cüzdanın/sözleşmenin adresi,
  • amount: gönderilecek token miktarı.

Account değeri sözleşmeyi çağıran cüzdanın/sözleşmenin adresi olan özel bir değişken olan msg.sender’dan alınır . Amount ise ondalık sayıları dikkate alması gerekiyor ve bu yüzden bu kadar büyük bir sayıyı geçiyoruz, biraz değinelim.

Ondalık Sayılar Üzerine Bir Not

Kripto para birimleriyle uğraşırken, 0.004ETH gibi keyfi miktarlar gönderebilmek isteyebilirsiniz. Ne yazık ki, Solidity ve Ethereum Sanal Makinesi ondalık sayıları desteklemez : yalnızca tamsayılar kullanılabilir . Bu, yalnızca tam sayıların (1, 4, 5) gönderilebileceği anlamına gelir ve bu, elbette bir sorun teşkil eder.

Peki geçici çözüm nedir?

Çok basit, bir token sözleşmesi daha büyük tamsayı değerleri kullanabilir (EVM 256-bit tamsayıları destekler), böylece 10000000000000000000 bakiyesi 18 ondalık basamaklı 1 ETH’yi temsil eder, dolayısıyla 4000000000000000'lık bir transfer gönderilen 0.004ETH’ye karşılık gelir.

Toplam arzımızı hesaplarken, sahip olmak istediğimiz ondalık basamaklar da dahil olmak üzere toplam token miktarını hesaba katmamız gerekir.

Ethereum ve diğer birçok kripto para biriminde olduğu gibi 18 ondalık basamaklı toplam maksimum 1.000.000.000 token arzı istiyorsanız, 1000000000*10**18'i (1000000000000000000000000000) geçmek istersiniz.

Öte yandan, 2 token gönderirken çağrılacak yöntem aslında şöyle olacaktır:

transfer(recipient, 2 * 10**18);

Tamam, şimdi mint fonksiyonunda neler olduğunu anladığımıza göre, sözleşmemizi test edelim.

İlk Kripto Paranızı Dağıtın

REMIX’te ekranın sol tarafındaki Solidity simgesine tıklayın ve Compile butonuna tıklayın. REMIX’in kod değişikliklerini, dinlemesine ve kodunuzu anlık derlemesine izin vermek için otomatik derlemeyi de etkinleştirmek isteyebilirsiniz.

Bu, Token.sol kodunu derleyecek, artifacts klasörünü Tokenların abi (application binary interface) formatları ve blok zincirinde dağıtmak için kullanılan ikili versiyonu ile dolduracaktır.

Artık yapıtlarımıza sahip olduğumuza göre, Solidity simgesinin altındaki Ethereum logosuna tıklayın, açılır menüden sözleşmenizi seçin ve Deploy’a tıklayın:

Tebrikler ! 🎉 Her şey beklendiği gibi çalıştıysa, ilk ERC20 / Kripto paranızı az önce dağıttınız.

Token ile Etkileşime Girin

Token dağıtıldığında (deploy edildiğinde), akıllı sözleşmemiz cüzdanımıza 1000 token vermeliydi! Remix de deploy düğmesinin hemen üzerinde “Account” alanı var:

Kırmızı çerçeveli alan token sözleşmemizi dağıtmak için kullandığımız cüzdanın adresidir. Cüzdana gelen tokenların eksik olduğunu görmüşsünüzdür. Sözleşmeyi Ethereum Sanal Makinesinde dağıtmak için ödediğimiz Gaz ücretlerinin olduğunu söyleyebilirim.

Her şey beklendiği gibi çalıştıysa, şimdi cüzdanımızın adresinin bakiyesinde verilen token miktarını görmeliyiz.

Sayfanın biraz aşağısında dağıtılan ERC20 tokenı göreceksiniz. Yandaki açılır menü butonuna tıklayıp ERC20 tokenın tüm methodlarını göreceksiniz:

Butonların rengi, temsil eden işlevin blok zincirindeki herhangi bir değeri değiştirip Gaz ücretine mal olup olmadığını ( Turuncu ) veya sadece bir değeri okuyup döndüren basit bir görüntüleme işlevini ( Mavi ) temsil eder.

Bu butonlar arasında “ balanceOf ” yazan giriş olarak bir adres bilgisi gerektiriyor ve ilgili ERC20 Token miktarını dönüyor.

Tebrikler , az önce ilk kripto para biriminizi yarattınız! 🎉

Ne yazık ki, şu anda iki sorun var:

  1. Token’ımıza henüz REMIX dışından erişilemiyor çünkü şu anda bir blok zinciri gibi davranan ve REMIX dışında hiçbir şey tarafından erişilemeyen bir sanal makine olan Remix EVM’de konuşlandırılıyor.
  2. Şu anda yaptığımız gibi tüm token ları dağıtımda yayınlamak yerine, bir eylemin sonucu olarak veya küçük gruplar halinde ödül olarak vermek isteyebiliriz .

İlk önce token arzının nasıl ele alınacağı konusuna değinelim .

Not : REMIX, bir test ağına kıyasla barındırılan bir sanal ethereum ortamı kullanır, ancak ortamın kendisi dışında herkese açık olarak erişilebilir değildir. Sözleşmeniz gerçek Ethereum ana ağında veya herkesin erişebileceği bir test ağında dağıtılmayacaktır.

Token arzınızı oluşturmak için 3 seçeneğimiz var:

  • Sabit Arz
  • Sınırsız Modüler Arz
  • Sınırlı Modüler Arz

Bu noktada, yukarıda yaptığımız anlatımda tokenımız dağıtımda verilen bir sabit arza sahiptir, bunun ne anlama geldiğini ve kripto para birimlerimizi geliştirirken sahip olduğumuz alternatifleri keşfedelim.

Sabit Arz

Tokenın toplam arzı dağıtımda verilir ve dağıtım cüzdan adresine gönderilir.

Bu durumda, önceden toplam arzı seçeriz ve sözleşmenin kurucusu (constructor) çağrıldığında tüm tutarı cüzdanımıza göndeririz ve bize tokenları farklı sahipler arasında dağıtma işlemi kalır.

Tüm tokenları bir kerede vermek istemiyorsak, modüler bir para basma yaklaşımını seçmeliyiz.

Sınırsız Modüler Arz

Tokenlar, dağıtım sırasında benzersiz bir toplu iş halinde basılmaz ve verilmez, ancak küçük miktarlarda ve belirtilen cüzdanlara, dolayısıyla bir veya daha fazla eyleme gönderilir.

Bu durumda, toplam maksimum arz, sabit kodlanmış bir değerle değil, ekonomik temelli ilkelerle düzenlenir.

Bir işlemi onaylayan ve tokenları ödül olarak geri alan bir Madenciyi veya periyodik ödüller almak için tokenları tutan (staking) bir kullanıcıyı düşünün.

Bununla birlikte, sabit kodlanmış bir Max arzının olmaması, tokenı enflasyonist hale getirebilir, zamanla değer kaybına neden olabilir veya güvenliğini en kötü şekilde riske atabilir. Bu, bu eğitimin odak noktasının dışına çıksa bile, arzımızı sınırlayabileceğimizi ve yapmamız gerektiğini anlamakta fayda var.

Sınırlı Modüler Arz

Sınırsız modüler arz mekanizmasında olduğu gibi, jetonlar küçük miktarlarda verilir, tek fark burada maksimum arzın önceden kararlaştırılması ve akıllı sözleşmede sabit kodlanması veya dağıtımda belirlenmesidir.

Bu eğitimde, tokenlarımızın arzını oluşturmak için tüm yöntemleri kapsamlı bir şekilde keşfedeceğiz.

Token Arzı — Sabit arz

Diyelim ki, başlangıçta sözleşmeyi dağıtan cüzdana tahsis edilen sabit 1000 adet token vermek istiyoruz istiyoruz (kodumuzda yaptığımız gibi), bunu yapmak için Token Oluşturucu içindeki _mint() fonksiyonunu çağırdık:

Örnek;

Sabit Arz Kodları : https://github.com/erayusta/basic_smart_contract/blob/main/Fixed_Supply.sol

Oluşturucunun (constructor) dağıtım sırasında yalnızca bir kez çağrıldığını bilen bu akıllı sözleşme, cüzdanımıza yalnızca 1000 token (tutarın 18 ondalık basamağı vardır: ondalık kısımlar bölümündeki nota bakın) aktarmakla kalmadı, aynı zamanda _mint() fonksiyonu sonradan çağırılır olmadığı için yeni token basmayı da imkansız hale getirdi.

Token ı dağıtan adresin token bakiyesini, daha önce yaptığımız gibi balanceOf() yöntemini çağırarak kontrol edebiliriz.

Peki, tokenları benzersiz bir toplu halde değil de zaman içinde vermek istersek ne olur?

Token Arzı — Sınırsız Modüler Arz

Bunu başarmak için mint işlevini kurucudan (constructor) farklı koşullar altında çağrılabilecek yeni bir fonksiyona taşıyabiliriz:

Örnek:

Sınırsız Modüler Arz: https://github.com/erayusta/basic_smart_contract/blob/main/Uncapped_Lazy_Suppyl-1.sol

Bu durumda, issueToken() her çağrıldığında, arayan kişi bu miktarda token alacaktır. Elbette, verilen jeton miktarını veya alıcıyı değiştirmek için argümanlar isteyebiliriz:

Örnek:

Argümanlı Sınırsız Modüler Arz: https://github.com/erayusta/basic_smart_contract/blob/main/Uncapped_Lazy_Supply-2.sol

Buradaki sorun, daha fazla kontrol olmaksızın, bir kullanıcının çıkarabileceği token sayısında bir sınırlama olmamasıdır. Geçici bir çözüm olarak, Token’in toplam arzını sınırlayabiliriz.

Token Temini — Sınırlı Modüler Arz

Tokena maksimum arz eklemek için ERC20Capped — OpenZeppelin kütüphanesini kullanabiliriz.

ERC20Capped, standart ERC20 sözleşmesinden miras kalan bir ERC20Mintable alt sınıfıdır. Bu, _mint() ve balanceOf() gibi yöntemleri korurken, kodumuzdaki ERC20.sol kitaplığını ERC20Capped ile değiştirmemize olanak tanır.

Ayrıca, Token kurucusuna (constructor) bir uint256 argümanı ileterek ve onu ERC20Capped kurucusunu (constructor) besleyerek bir üst sınır değeri belirtmemiz gerekecek.

ERC20Capped kütüphanesini OpenZeppelin’den import edelim ve aşağıdaki kodu Token.sol sözleşmesine ekleyelim:

Örnek:

Sınırlı Modüler Arz: https://github.com/erayusta/basic_smart_contract/blob/main/Capped_Modular_Supply.sol

Artık, daha önce yaptığımız gibi, REMIX kullanarak Capped belirtecimizi test edebiliriz. 5 adet Nilcoin arz sınırı aşıldığında “sınır aşıldı (cap exceeded)” hatası verip arz almamızı engelleyecektir.

Artık basılabilir bir tokenımız olduğuna göre, çözülmesi gereken bir sorun kaldı: herkes onu issueToken() işlevini çağırarak basabilir ve bu çok büyük bir güvenlik sorununa yol açar. Ne mutlu ki buna da bir çözüm var: Sahip olunan sözleşmeler (owned contracts).

Kripto Paranızı Güvenli Hale Getirin — Erişim Kontrolü

Erişim kontrolüne ilişkin OpenZeppelin belgelerinin bize söylediklerine bir bakalım:

Akıllı sözleşmelerle uğraşırken erişim kontrolü veya “bu şeyi kimin yapmasına izin verilir” inanılmaz derecede önemlidir. Sözleşmenizin erişim denetimi, kimlerin token basabileceğini, tekliflere oy verebileceğini, transferleri dondurabileceğini ve diğer birçok şeyi yönetebilir.

Bu nedenle, başka birinin tüm sisteminizi çalmaması için onu nasıl uyguladığınızı anlamak çok önemlidir.

Erişim kontrolünün en yaygın ve temel biçimi, bir sözleşmede hassas görevleri yerine getirmesine izin verilen bir hesabın (owner — sahip) olduğu sahiplik kavramıdır. Bu yaklaşım, tek bir yönetici kullanıcıya sahip sözleşmeler için tamamen mantıklıdır.

OpenZeppelin, sözleşmelerde mülkiyetin uygulanmasını Ownable ile sağlar.

Bunu kullanmak için, sözleşmeyi kodumuza aktarmamız, daha önce yaptığımız gibi Token sözleşmemizin bir süper sınıfı (super-class) olarak eklememiz ve issueToken() a bir işlev değiştirici (modifier) eklememiz gerekecek:

Örnek:

Erişim Kontrolü: https://github.com/erayusta/basic_smart_contract/blob/main/Access_Control_Contract.sol

Fark edebileceğimiz gibi, Token sözleşmesi şimdi hem ERC20'den hem de Ownable sözleşmesinden miras alıyor, bu bize issueToken() işlevine uygulanan onlyOwner işlev değiştiricisine (function modifier) erişim sağlıyor.

onlyOwner, issueToken() her çağrıldığında, arayanın sözleşmenin sahibi olup olmadığını doğrulayarak çalışır.

Varsayılan olarak, Sahip olunan bir sözleşmenin sahibi, onu dağıtan hesaptır ve bu genellikle tam olarak istediğiniz şeydir.

Artık token arzımızı nasıl tasarlayacağımıza ve onun basım sürecini nasıl güvence altına alacağımızı öğrendik.

Konuyu youtube da video olarak da anlattım:

Bir sonraki yazıda ise Hardhat ile yerel (local) ortam kurup bunu Polygon, Ethereum ve BSC ağlarına gönderdim. Hatta daha fazlasını yapıp a dan z ye herşeyi anlattım :) Okumak için buradan devam edebilirsiniz

Daha fazla bilgi ve kaynak için twitter hesabımdan beni takip edebilirsiniz.

--

--