Yeni ve Acemi Geliştiriciler için Solidity Hakkında Her şey

Eray USTA
29 min readFeb 22, 2022

Bu yazıda size Solidity ile akıllı kontrat geliştirmek için başlangıç kabul edilecek geniş kapsamlı çeviri yazmak istedim. Yazıyı okuduktan sonra Solidity özelinde ilk adımları rahatça atabilecek ve dil hakkında hemen hemen tüm bilgilere sahip olacaksınız. Aslında niyetim başucu kaynağı oluşturmaya çalışmak olacaktır. (Bazı kısımlarda ingilizce karşılıklarına uygun tanımlamaları bulamadığım için doğrudan yazmak zorunda kaldım.) Başlayalım 👇

Solidity Nedir?

Solidity, akıllı sözleşmeleri uygulamak için sözleşme odaklı, üst düzey bir programlama dilidir. Solidity, C++, Python ve JavaScript’ten oldukça etkilenir ve Ethereum Virtual Machine’i (EVM) hedeflemek için tasarlanmıştır.

Bu eğitim, acemi geliştiricileri ve Solidity’de yeni olanları hedefler. Solidity’deki daha gelişmiş kavramlar aracılığıyla temelleri anlamalarına rehberlik eder. Bu öğreticiyi tamamladıktan sonra, Solidity’deki sağlam temeliniz ve uzmanlık düzeyiniz, gelişmeye başlamanıza ve bilginizi kolayca geliştirmenize olanak sağlayacaktır.

Okuduktan sonra yeni çeviriye başladığım https://orneklerle-solidity.vercel.app/ bu linkten örneklerle solidity öğrenmeye devam edebilirsiniz.

Solidity statik olarak yazılır, kalıtımı, kitaplıkları ve karmaşık kullanıcı tanımlı türleri programlama dilini destekler.

Oylama, kitle fonlaması, kör müzayedeler ve çok imzalı cüzdanlar gibi kullanımlar için sözleşmeler oluşturmak için Solidity’yi kullanabilirsiniz.

Ethereum nedir?
Ethereum, merkezi olmayan akıllı sözleşmeler yürüten blok zinciri platformu, yani herhangi bir kesinti, sansür, dolandırıcılık veya üçüncü taraf müdahalesi olasılığı olmadan tam olarak programlandığı gibi çalışan uygulamalardır.

Ethereum Sanal Makinesi (EVM)
EVM olarak da bilinen Ethereum Sanal Makinesi, Ethereum’daki akıllı sözleşmeler için çalışma zamanı ortamıdır. Ethereum Sanal Makinesi, dünyanın her yerindeki bilgisayarlar tarafından güvenlik sağlamaya ve güvenilmeyen kod yürütmeye odaklanır.

EVM, Hizmet Reddi saldırılarını önleme konusunda uzmanlaşmıştır ve programların birbirlerinin durumuna erişmemesini sağlayarak herhangi bir potansiyel müdahale olmadan iletişimin kurulabilmesini sağlar.

Ethereum Sanal Makinesi, Ethereum’a dayalı akıllı sözleşmeler için bir çalışma zamanı ortamı olarak hizmet etmek üzere tasarlanmıştır.

Akıllı Sözleşme nedir?
Akıllı sözleşme, bir sözleşmenin müzakeresini veya performansını dijital olarak kolaylaştırmayı, doğrulamayı veya zorlamayı amaçlayan bir bilgisayar protokolüdür. Akıllı sözleşmeler, üçüncü taraflar olmadan güvenilir işlemlerin gerçekleştirilmesine olanak tanır. Bu işlemler izlenebilir ve geri alınamaz.

Akıllı sözleşmeler kavramı ilk olarak 1994 yılında Nick Szabo tarafından önerildi. Szabo, dijital para biriminin temelini atmakla tanınan bir hukuk bilgini ve kriptografi uzmanıdır.

Şu anda Akıllı Sözleşmeyi anlamadıysanız sorun değil, daha sonra daha fazla ayrıntıya gireceğiz.

Çalışma Ortamı Kurulumu

Bu bölüm, CentOS makinesinde Solidity derleyicisini nasıl kurabileceğimizi açıklar. Bir Linux makineniz yoksa, küçük sözleşmeler için ve Solidity’yi hızlı bir şekilde öğrenmek için Remix IDE kullanabilirsiniz.

Yöntem 1 — npm / Node.js

Bu, Solidity derleyicisini CentoS Makinenize kurmanın en hızlı yoludur. Solidity Compiler’ı kurmak için aşağıdaki adımlara sahibiz -

Node.js’yi yükleyin
Öncelikle, CentOS makinenizde node.js bulunduğundan emin olun. Mevcut değilse, aşağıdaki komutları kullanarak kurun -

# İlk önce epel-release’i kurun
$sudo yum install epel-release
# Şimdi node.js kurun
$sudo yum install nodejs
# Sonraki kurulum npm (Nodejs Paket Yöneticisi)
$sudo yum install npm
# Sonunda kurulumu doğrulayın
$npm --version

Her şey yüklendiyse, bunun gibi bir çıktı göreceksiniz -

3.10.10

Solc’u yükle

Node.js paket yöneticisini kurduktan sonra, aşağıdaki gibi Solidity derleyicisini kurmaya devam edebilirsiniz -

$sudonpm install -g solc

Yukarıdaki komut solcjs programını kuracak ve onu sistem üzerinden global olarak kullanılabilir hale getirecektir. Artık aşağıdaki komutu vererek Solidity derleyicinizi test edebilirsiniz.

$solcjs-version

Her şey yolunda giderse, bu aşağıdaki gibi bir şey yazdıracaktır -

0.5.2+commit.1df8f40c.Emscripten.clang

Artık standart Solidity derleyicisinden daha az özelliğe sahip olan solcj’leri kullanmaya hazırsınız, ancak bu size iyi bir başlangıç ​​noktası sağlayacaktır.

Yöntem 2 — Docker Görüntüsü

Bir Docker görüntüsü çekebilir ve Solidity programlamaya başlamak için kullanmaya başlayabilirsiniz. Basit adımlar aşağıdadır. Solidity Docker Image çekme komutu aşağıdadır.

$docker pull ethereum/solc:stable

Bir docker görüntüsü indirildikten sonra, aşağıdaki komutu kullanarak onu doğrulayabiliriz.

$docker run ethereum/solc:stable-version

Bu, aşağıdaki gibi bir şey yazdıracaktır -

$ docker run ethereum/solc:stable -version

solc, the solidity compiler commandlineinterfaceVersion: 0.5.2+commit.1df8f40c.Linux.g++

Yöntem 3: Binary Paketlerin Kurulumu
Linux makinenize derleyici kurmak istiyorsanız, lütfen resmi web sitesine bakın.

Temel Yazım Stili

Bir Solidity kaynak dosyaları, herhangi bir sayıda sözleşme tanımını, içe aktarma yönergelerini ve pragma yönergelerini içerebilir.

Basit bir Solidity kaynak dosyasıyla başlayalım. Aşağıda bir Solidity dosyası örneği verilmiştir.

Pragma

İlk satır, kaynak kodunun Solidity sürüm 0.4.0 veya 0.6.0 sürümüne kadar işlevselliği bozmayan ancak bu sürümü içermeyen daha yeni herhangi bir şey için yazıldığını söyleyen bir pragma yönergesidir.

Bir pragma yönergesi her zaman bir kaynak dosyada yereldir ve başka bir dosyayı içe aktarırsanız, o dosyadan gelen pragma, içe aktarılan dosyaya otomatik olarak uygulanmaz.

Bu nedenle, 0.4.0 sürümünden önce derlenmeyecek ve 0.5.0 sürümünden itibaren derleyici üzerinde çalışmayacak bir dosyanın pragması aşağıdaki gibi yazılacaktır -

pragma solidity ^0.4.0;

Burada ikinci koşul ^ kullanılarak eklenir.

Sözleşme

Bir Solidity sözleşmesi, Ethereum blockchain üzerinde belirli bir adreste bulunan bir kod (function) ve veri (state) koleksiyonudur.

Yukardaki örnekte uint storedData satırı, uint türünde storedData adlı bir durum değişkeni bildirir ve set ve get işlevleri, değişkenin değerini değiştirmek veya almak için kullanılabilir.

Dosyaları İçe Aktarma

Yukarıdaki örnekte bir içe aktarma ifadesi olmamasına rağmen Solidity, JavaScript’te bulunanlara çok benzeyen içe aktarma ifadelerini destekler.

Aşağıdaki ifade, tüm global sembolleri “dosya adından” alır.

import "filename";

Aşağıdaki örnek, üyelerinin tümü “filename”den global symbol olan yeni bir global symbolName oluşturur.

import * as symbolName from "filename";

Geçerli dosyayla aynı dizinden bir x dosyasını içe aktarmak için, import “./x” as x; kullanın. “x”i x olarak içe aktarırsanız; bunun yerine, global bir “include directory” farklı bir dosyaya başvurulabilir.

Solidity ye Ayrılmış Anahtar Kelimeler

İlk Uygulama

Solidity Code tabanımızı Derlemek ve Çalıştırmak için Remix IDE kullanıyoruz.

Adım 1 − Remix IDE Code Bölümünde verilen kodu kopyalayın.

Adım 2 − Compile Sekmesi altında, Start to Compile düğmesine tıklayın.

Adım 3 − Çalıştır Sekmesi altında, Deploy düğmesine tıklayın.

Adım 4 − Çalıştır Sekmesi altında, açılır menüden 0x’te SolidityTest… öğesini seçin.

Adım 5 − Sonucu görüntülemek için getResult Düğmesine tıklayın.

Çıktısı

0: uint256: 3

Yorum Satırı

Solidity, hem C tarzı hem de C++ tarzı yorumları destekler, Bu nedenle -

// ile satırın sonu arasındaki herhangi bir metin yorum olarak kabul edilir ve Solidity Derleyicisi tarafından yok sayılır.

/* ve */ karakterleri arasındaki herhangi bir metin yorum olarak kabul edilir. Bu, birden fazla satıra yayılabilir.

Örnek:
Aşağıdaki örnek, Solidity’de yorumların nasıl kullanılacağını gösterir.

Veri Tipleri

Herhangi bir dilde program yazarken, çeşitli bilgileri saklamak için çeşitli değişkenler kullanmanız gerekir. Değişkenler, değerleri depolamak için ayrılmış bellek konumlarından başka bir şey değildir. Bu, bir değişken oluşturduğunuzda bellekte biraz yer ayırdığınız anlamına gelir.

Karakter, tamsayı , boolean vb. gibi çeşitli veri türlerinin bilgilerini depolamak isteyebilirsiniz. Bir değişkenin veri türüne bağlı olarak, işletim sistemi bellek ayırır ve neyin depolanabileceğine karar verir.

  • Değer Türleri (Value Type)
  1. Boolean(true / false)
  2. Integer (int / unit)
  3. Address(size of Ethereum address)
  4. String
  5. enums
  • Referans Türleri (Reference Types)
  1. Arrays (Diziler)
  2. Struct (Yapılar)
  3. Mapping

Değişkenler

Yerel Değişkenler (Local Variables): Fonksiyon tamamlanana kadar devam edecek değerleri olan değişkenlerdir.

Durum Değişkenleri (State Variables): Değerleri bir sözleşme depolama sisteminde kalıcı olarak tutulan değişkenlerdir.

Global Değişkenler (Global Variables): Blok zinciri hakkında bilgi almak için kullanılan global ad alanında özel değişkenlerdir.

Solidity, statik olarak yazılmış bir dildir; bu, bildirim sırasında durum veya yerel değişken türünün belirtilmesi gerektiği anlamına gelir. Bildirilen her değişken, türüne göre her zaman varsayılan bir değere sahiptir. “Tanımsız” veya “boş” kavramı yoktur.

Global Değişkenler

Solidity Değişken Adları

Solidity’de değişkenlerinizi adlandırırken aşağıdaki kuralları aklınızda bulundurun.

  • Değişken adı olarak Solidity ayrılmış anahtar kelimelerin hiçbirini kullanmamalısınız. Bu anahtar kelimeler bir sonraki bölümde belirtilmiştir. Örneğin, break veya boolean değişken adları geçerli değildir.
  • Solidity değişken isimleri bir rakamla (0–9) başlamamalıdır. Bir harf veya alt çizgi karakteri ile başlamalıdırlar. Örneğin, 123test geçersiz bir değişken adıdır, ancak _123test geçerli bir değişkendir.
  • Solidity değişken isimleri büyük/küçük harf duyarlıdır. Örneğin, Ad ve ad iki farklı değişkendir.

Değişken Kapsamı

Yerel değişkenlerin kapsamı, tanımlandıkları fonksiyon ile sınırlıdır, ancak Durum (state) değişkenlerinin üç tür kapsamı olabilir.

Genel (Public) − Genel durum değişkenlerine hem dahili olarak hem de mesajlar yoluyla erişilebilir. Bir genel durum değişkeni için otomatik bir alıcı işlevi oluşturulur.

Dahili (Internal)− Dahili durum değişkenlerine, bunu kullanmadan yalnızca mevcut sözleşmeden veya ondan türetilen sözleşmeden dahili olarak erişilebilir.

Özel (Private)− Özel durum değişkenlerine, türetilmiş sözleşmede tanımlanmadıkları mevcut sözleşmeden yalnızca dahili olarak erişilebilir.

Operatörler

Operatörler, programlamanın temelini oluşturdukları için her programlama dilinde önemlidir. Benzer şekilde, Solidity’nin işlevselliği de operatörler kullanılmadan eksik kalır.

A’yı 10 B’yi 20 kabul ederek operatörleri inceleyelim

Aritmetik Operatörler

  • + (toplama) Ör: A + B = 30
  • - (çıkarma) Ör: A — B = -10
  • * (çarpma) Örn: A * B = 200
  • / (bölme) Ör: B / A = 2
  • % (Mod) Ör : B % A = 0
  • ++ (Arttırma) Ör: A++ = 11
  • — (Azaltma) Ör : A — = 9

İlişkisel Operatörler

== (Eşittir) Ör: (A == B) yanlış (false)

!= (Eşit değildir) Ör: (A != B) doğru (true)

> (Büyüktür) Ör: (A > B) yanlış (false)

< (Küçüktür) Ör: (A < B) doğru (true)

>= (Büyük veya eşittir) Ör: (A >= B) yanlış (false)

<= (Küçük veya eşittir) Ör: (A <= B) doğru (true)

Mantıksal Operatörler

&& (Ve) Ör: A && B doğru (true)

|| (Veya) Ör: A|| B doğru (true)

! (Değildir) Ör: ! (A && B) yanlış (false)

Bitwise Operatörleri

A yı 2 B yi 3 alarak karşılaştıralım.

& (Bitwise VE) : Tamsayı argümanlarının her bitinde bir Boolean AND işlemi gerçekleştirir.
Örn: (A & B) 2'dir.

| (Bitwise VEYA) : Tamsayı argümanlarının her bitinde bir Boolean OR işlemi gerçekleştirir.
Örn: (A | B) 3'tür.

^ (Bitwise XOR): Tamsayı bağımsız değişkenlerinin her bitinde bir Boolean özel VEYA işlemi gerçekleştirir. Özel VEYA, bir işlenenin doğru olduğu veya işlenen ikinin doğru olduğu, ancak ikisinin birden olmadığı anlamına gelir.
Örn: (A ^ B) 1'dir.

~ (Bitwise Değil): Tekli bir operatördür ve işlenendeki tüm bitleri tersine çevirerek çalışır.
Örn: (~B) -4'tür.

<< (Sol 0 Kaydırma): İlk işlenenindeki tüm bitleri, ikinci işlenende belirtilen basamak sayısı kadar sola taşır. Yeni bitler sıfırlarla doldurulur. Bir değeri bir konum sola kaydırmak, onu 2 ile çarpmaya eşdeğerdir, iki konumu kaydırmak, 4 ile çarpmaya eşdeğerdir, vb.
Ör: (A << 1) 4'tür.

>> (Sağa 0 Kaydırma) : İkili Sağ Kaydırma Operatörü. Sol işlenenin değeri, sağ işlenen tarafından belirtilen bit sayısı kadar sağa taşınır.
Ör: (A >> 1) 1'dir.

>>> (Sıfır ile sağa kaydırma): Bu operatör, sola kaydırılan bitlerin her zaman sıfır olması dışında, >> operatörü gibidir.
Ör: (A >>> 1) 1'dir.

Atama Operatörleri

= (eşittir) Ör: C = A+B yani A+B yi C ye atar.

+= (Topla ve eşitle) Ör: C+=A yani C=C+A’dır.

-= (Eksilt ve eşitle) Ör: C -= A yani C=C-A’dır

*= (Çarp ve eşitle) Ör: C *= A yani C=C*A’dır

/= (Böl ve eşitle) Ör: C /=A yani C= C/A’dır

%= (Mod al ve eşitle) Ör: C %=A yani C = C %A’dır

Koşullu Operatörler

Koşul operatörü önce bir ifadeyi doğru veya yanlış bir değer için değerlendirir ve ardından değerlendirmenin sonucuna bağlı olarak verilen iki ifadeden birini yürütür.

? : (Conditional )

uint a = 1; // local variable
uint b = 2;
uint result = (a > b? a: b);

Örnekteki ifadede a, b’den büyükse a değilse b olarak değişkene atama yapar.

Döngüler

While loop

Bir ifade doğru olduğu sürece içerisindeki kod bloğunu tekrar yürütür. İfade yanlış olduğunda döngü sona erer.

Kaynak: https://www.tutorialspoint.com/solidity/solidity_while_loops.htm

while (expression) {
// doğru olduğu sürece döngü döner
}

while (_i != 0) { // while loop
_i /= 2;
}

do…while Loop

do…while döngüsü, koşul denetiminin döngünün sonunda gerçekleşmesi dışında while döngüsüne benzer. Bu, koşul yanlış olsa bile döngünün her zaman en az bir kez yürütüleceği anlamına gelir.

do {
// Statement(s) to be executed;
} while (expression);

do { // do while loop
_i /= 10;
}
while (_i != 0);

For Loop

For döngüsü, döngülerin en kompakt şeklidir. Aşağıdaki üç önemli bölümü içerir -

  1. Sayacımızı bir başlangıç değerine başlattığımız döngü başlatma. Başlatma ifadesi, döngü başlamadan önce yürütülür.
  2. Belirli bir koşulun doğru olup olmadığını test edecek test ifadesi. Eğer koşul doğru ise döngü içinde verilen kod çalıştırılır, aksi halde kontrol döngüden çıkar.
  3. Sayacınızı artırabileceğiniz veya azaltabileceğiniz yineleme ifadesi.
for (başlatma ifadesi; test ifadesi; sayacı arttırma veya azaltma ifadesi) {
Statement(s) to be executed if test condition is true
}
for (j = 0; j < 10; j++) { //for loop example
// işlemler gerçekleştir
}

Loop Control

Solidity, döngüleri işlemek ve ifadeleri değiştirmek için tam kontrol sağlar. Bir döngüden sona gelmeden çıkmanız gereken bir durum olabilir. Ayrıca, kod bloğunuzun bir bölümünü atlamak ve döngünün bir sonraki yinelemesini başlatmak istediğiniz bir durum olabilir.

Tüm bu tür durumları ele almak için Solidity, break ve continue ifadeleri sağlar. Bu ifadeler herhangi bir döngüden hemen çıkmak veya herhangi bir döngünün bir sonraki yinelemesini başlatmak için kullanılır.

while( n < 10){
n++;
if(n == 5){
continue; // n 5 e eşit ise sum ile toplamayıp başa döner
}
sum = sum + n;
}
return integerToString(sum);
while (true) {
len++;
j /= 10;
if(j==0){
break; //j 10 a eşit olduğunda döngü sona erer.
}
}

Koşullu İfadeler

Bir program yazarken, belirli bir yol kümesinden birini benimsemeniz gereken bir durum olabilir. Bu gibi durumlarda, programınızın doğru kararlar almasına ve doğru eylemleri gerçekleştirmesine olanak tanıyan koşullu ifadeler kullanmanız gerekir.

Solidity, farklı koşullara dayalı olarak farklı eylemleri gerçekleştirmek için kullanılan koşullu ifadeleri destekler. Burada if..else ifadesini açıklayacağız.

if (koşul 1) {
koşul 1 sağlanıyorsa burdaki ifadeler çalışır
} else if (koşul 2) {
koşul 2 ise buradaki ifadeler çalışır
} else {
koşul hiç bir if bloğu ile eşleşmiyorsa bu örnek için 1 ve 2 değilse her zaman burası çalışır.
}
uint a = 1;
uint b = 2;
uint c = 3;
uint result

if( a > b && a > c) { // if else statement
result = a;
} else if( b > a && b > c ){
result = b;
} else {
result = c;
}
// Yukardaki örnekte eğer a b'den büyükse ve a c'den büyükse result değişkenine a değerini verir.
// 2. koşulda ise b a'dan büyükse ve b c'den büyükse result değişkenine b değerini verir
// İki koşulda sağlanmıyorsa result'a c değişkenini atar.
// Bu örnek için else bloğu çalışacaktır.

Strings

Solidity, hem çift tırnak (“) hem de tek tırnak (‘) kullanarak String değişkenleri destekler. String türünde bir değişken bildirmek için veri türü olarak dize sağlar.

pragma solidity ^0.5.0;contract SolidityTest {
string data = “test”;
}

Yukarıdaki örnekte, “test” bir string değişkenidir. Daha çok tercih edilen yol, string işlemi byte işlemine göre daha fazla gas gerektirdiğinden String yerine byte türlerini kullanmaktır. Solidity, bytelar arasında dizeye ve tam tersine yerleşik dönüştürme sağlar. Solidity’de bir byte32 tipi değişkene String değişmezini kolayca atayabiliriz. Solidity, bunu bir byte32 değişmezi olarak kabul eder.

pragma solidity ^0.5.0;

contract SolidityTest {
bytes32 data = "test";
}

Stringlerde Escape Karakterler

\n — Yeni Satır
\\ — Backslash
\’ — String içinde tırnak kullanılmak istenirse
\” — String içinde çift tırnak kullanılmak istenirse
\t — tab

Byte değişkeni String e çevirme

Bayt, string() yapıcısı kullanılarak String’e dönüştürülebilir.

bytes memory byteString = new bytes(10);
string message = string(byteString);

Diziler

Dizi, aynı türdeki öğelerin sabit boyutlu sıralı bir koleksiyonunu depolayan bir veri yapısıdır. Bir dizi, bir veri koleksiyonunu depolamak için kullanılır, ancak bir diziyi aynı türdeki değişkenlerin bir koleksiyonu olarak düşünmek genellikle daha yararlıdır.

sayı0, sayı1, … ve sayı99 gibi tek tek değişkenleri bildirmek yerine, sayılar gibi bir dizi değişkeni bildirir ve temsil etmek için sayıları[0], sayıları[1] ve …, sayıları[99] kullanırsınız. Dizideki belirli bir öğeye bir dizin tarafından erişilir.

Solidity’de bir dizi, derleme zamanı sabit boyutunda veya dinamik boyutta olabilir. Depolama dizisi için farklı türde öğelere de sahip olabilir. Bellek dizisi olması durumunda, eleman tipi eşlenemez ve fonksiyon parametresi olarak kullanılması durumunda eleman tipi bir ABI tipi olmalıdır.

Tüm diziler bitişik bellek konumlarından oluşur. En düşük adres ilk öğeye, en yüksek adres ise son öğeye karşılık gelir.

Dizileri Tanımlamak
Solidity’de sabit boyutlu bir dizi bildirmek için, programcı bir dizinin gerektirdiği öğelerin türünü ve öğelerin sayısını aşağıdaki gibi belirtir.

type arrayName [ arraySize ];

Buna tek boyutlu dizi denir. arraySize sıfırdan büyük bir tamsayı sabiti olmalıdır ve tür herhangi bir geçerli Solidity veri türü olabilir. Örneğin, uint türünde Balance adlı 10 elemanlı bir diziyi bildirmek için bu ifadeyi kullanırız.

uint balance[10];

Solidity’de bir dinamik boyut dizisi bildirmek için programcı, öğelerin türünü aşağıdaki gibi belirtir.

type[] arrayName;

Dizileri Değer Atama

Solidity dizisi öğelerini tek tek veya aşağıdaki gibi tek bir ifade kullanarak başlatabilirsiniz.

uint balance[3] = [1, 2, 3];

[ ] parantezleri arasındaki değerlerin sayısı, köşeli parantezler [ ] arasındaki dizi için bildirdiğimiz eleman sayısından fazla olamaz. Aşağıda, dizinin tek bir öğesini atamak için bir örnek verilmiştir.

Dizinin boyutunu atlarsanız, yalnızca başlatmayı tutacak kadar büyük bir dizi oluşturulur.

uint balance[] = [1, 2, 3];

Önceki örnekte yaptığınız gibi tam olarak aynı diziyi oluşturacaksınız.

balance[2] = 5;

Yukarıdaki ifade, dizideki 3. öğeye 5 değerini atar. Diziler [0] ıncı elemandan başlar.

Dinamik bellek dizileri oluşturma

Dinamik bellek dizileri, new anahtar sözcüğü kullanılarak oluşturulur.

uint size = 3;
uint balance[] = new uint[](size);

Dizi Öğelerine Erişim

Dizi adının indekslenmesiyle bir elemana erişilir. Bu, dizinin adından sonra öğenin indeksini köşeli parantezler içine alarak yapılır. Örneğin:

uint salary = balance[2];

Yukarıdaki ifade diziden 3. elemanı alacak ve değeri salary değişkenine atayacaktır. Aşağıda, yukarıda belirtilen üç kavramın tümünü kullanacak olan bir örnek verilmiştir. bildirim, atama ve dizilere erişim -

Ek özellikler
length — uzunluk dizinin boyutunu döndürür. uzunluk, ayarlanan dinamik dizinin boyutunu değiştirmek için kullanılabilir.

push − push, sonunda bir dinamik depolama dizisine bir öğe eklemeye izin verir. Dizinin yeni uzunluğunu döndürür.

Enums

Enum, bir değişkeni yalnızca birkaç önceden tanımlanmış değerden birine sahip olacak şekilde kısıtlar. Bu numaralandırılmış listedeki değerlere enums denir.

Enums kullanımı ile kodunuzdaki hata sayısını azaltmak mümkündür.

Örneğin, taze meyve suyu dükkanı için bir başvuru düşünürsek, cam boyutunu küçük, orta ve büyük olarak sınırlamak mümkün olacaktır. Bu, kimsenin küçük, orta veya büyük dışında herhangi bir boyut sipariş etmesine izin vermeyeceğinden emin olur.

pragma solidity ^0.8.7;

contract test {
enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
FreshJuiceSize choice;
FreshJuiceSize constant defaultChoice = FreshJuiceSize.MEDIUM;

function setLarge() public {
choice = FreshJuiceSize.LARGE;
}
function getChoice() public view returns (FreshJuiceSize) {
return choice;
}
function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}}

Yukarıdaki örneği Remix IDE’de çalıştırdığınızda önce SetLarge ardından getChoice seçeneğini çağırırsanız size 2 değerini dönecektir. Yani Enum değişkeni içindeki son elemanı (2 indise sahip eleman). Eğer getDefaultChoice seçeneğini çağırırsanız MEDIUM değerinin indisi olan 1'i dönecektir.

uint256: 1

Structs

Yapı türleri bir kaydı temsil etmek için kullanılır. Kitaplarınızın kaydını bir kütüphanede tutmak istediğinizi varsayalım. Her kitapla ilgili aşağıdaki özellikleri izlemek isteyebilirsiniz.

  • Title
  • Author
  • Subject
  • Book ID

Struct Tanımlama

struct struct_name { 
type1 type_name_1;
type2 type_name_2;
type3 type_name_3;
}

Örnek

struct Book { 
string title;
string author;
uint book_id;
}

Struct ve Değişkenlerine Erişme

Bir struct ın herhangi bir üyesine erişmek için üye erişim operatörünü (.) kullanırız. Üye erişim operatörü, yapı değişkeni adı ile erişmek istediğimiz yapı üyesi arasında bir nokta olarak kodlanmıştır. Yapı türündeki değişkenleri tanımlamak için yapıyı kullanırsınız. Aşağıdaki örnek, bir programda bir yapının nasıl kullanılacağını gösterir.

pragma solidity ^0.8.7;

contract test {
struct Book {
string title;
string author;
uint book_id;
}
Book book;

function setBook() public {
book = Book('Learn Java', 'TP', 1);
}
function getBookId() public view returns (uint) {
return book.book_id;
}
}

Örnek kodu Remix Ide ye yapıştırıp önce compile sonra deploy ettikten sonra setBook butonuna tıklayın. Ardından getBookId dediğinizde size atadığınız kitabın ID değerini döndürecektir.

Mapping

Mapping, diziler (array) ve yapılar (struct) gibi bir referans türüdür. Bir mapping bildirmek için sözdizimi aşağıdadır.

mapping(_KeyType => _ValueType)

Neresi

  • _KeyType — herhangi bir yerleşik tür byte ve dize olabilir. Referans türü veya karmaşık nesnelere izin verilmez.
  • _ValueType — herhangi bir tür olabilir.

Eşleme yalnızca depolama türüne sahip olabilir ve genellikle durum (state) değişkenleri için kullanılır.

Mapping genel olarak işaretlenebilir. Solidity otomatik olarak bunun için alıcı (getter) yaratır.

pragma solidity ^0.8.7;

contract LedgerBalance {
mapping(address => uint) public balances;

function updateBalance(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}
contract Updater {
function updateBalance() public returns (uint) {
LedgerBalance ledgerBalance = new LedgerBalance();
ledgerBalance.updateBalance(10);
return ledgerBalance.balances(address(this));
}
}

Yukarıdaki örnekte address leri balance lar ile mapping yapar. UpdateBalance butonuna tıkladığınızda çıktı olarak 10 değerini verecektir.

Dönüştürmeler

Solidity, örtük ve açık dönüştürmeye izin verir. Solidity derleyici, örtük dönüştürmenin mümkün olmaması ve bilgi kaybı olmaması koşuluyla iki veri türü arasında örtük dönüştürmeye izin verir. Örneğin uint8, uint16'ya dönüştürülebilir. ancak int8, uint256'ya dönüştürülemez. Çünkü int8, uint256'da izin verilmeyen negatif değerler içerebilir.

Örnek:

int8 y = -3;
uint x = uint(y);
//Now x = 0xfffff..fd == two complement representation of -3 in 256 bit format.
//Daha yüksek türe dönüştürme, sola dolgu bitleri ekler.
uint16 a = 0x1234;
uint32 b = uint32(a); // b = 0x00001234
// Daha büyük byte a dönüştürme, sağa dolgu bitleri ekler.
bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b = 0x12340000

Ether Birimleri

Solidity de çeşitli eter tabanlı dönüştürmek için kullanılacak bir değişmeze son ek olarak wei, finney, szabo veya eter kullanabiliriz. En düşük birim wei’dir ve 1e12, 1 x 1012'yi temsil eder.

assert(1 wei == 1);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);
assert(2 ether == 2000 fenny);

Zaman Birimleri

assert(1 seconds == 1);
assert(1 minutes == 60 seconds);
assert(1 hours == 60 minutes);
assert(1 day == 24 hours);
assert(1 week == 7 days);

Stil Klavuzu

Stil Kılavuzu, kod düzenini tutarlı tutmaya ve kodu daha okunaklı hale getirmeye yardımcı olur. Solidity ile sözleşme yazarken izlenen en iyi uygulamalar aşağıdadır.

Kod Düzeni

  • Girinti — Girinti seviyesini korumak için sekme yerine 4 boşluk kullanın. Boşlukları sekmelerle karıştırmaktan kaçının.
  • İki Boş Satır Kuralı − İki sözleşme tanımı arasında 2 Boş satır kullanın.
  • Bir Boş Satır Kuralı − İki fonksyion arasında 1 Boş satır kullanın. Yalnızca atama durumunda boş satırlara gerek yoktur.
  • Maksimum Satır Uzunluğu — Okuyucuların kodu kolayca ayrıştırabilmeleri için tek bir satır 79 karakteri geçmemelidir.
  • Sarma kuralları − İlk argüman parantez açmadan yeni satırda olsun. Bağımsız değişken başına tek girinti kullanın. Sonlandırma öğesi ); sonuncusu olmalıdır.
  • Kaynak Kodu Kodlaması — Tercihen UTF-8 veya ASCII kodlaması kullanılmalıdır.
  • Imports — Import ifadeleri, pragma beyanından hemen sonra dosyanın en üstüne yerleştirilmelidir.
  • Fonksiyonların Sırası — İşlevler, görünürlüklerine göre gruplandırılmalıdır.
  • Fazladan boşluklardan kaçının — Parantez, parantez veya parantez içinde hemen boşluklardan kaçının.
  • Kontrol yapıları — Parantezler, bildirimle aynı satırda açılmalıdır. Aynı girintiyi koruyarak kendi satırlarında kapatın. Açma desteği olan bir boşluk kullanın.
  • Fonksiyon Bildirimi — Parantezler için yukarıdaki kuralı kullanın. Her zaman bir görünürlük etiketi ekleyin. Görünürlük etiketi, herhangi bir özel değiştiriciden önce gelmelidir.
  • Mapping — Mapping değişkenlerini bildirirken boşluklardan kaçının.
  • Değişken bildirimi — Dizi değişkenlerini bildirirken boşluklardan kaçının.
  • Dize bildirimi — Bir dize bildirmek için tek tırnak yerine çift tırnak kullanın.

Düzen Sırası

Elemanlar aşağıdaki sıraya göre düzenlenmelidir.

  • Pragma ifadeleri
  • Import ifadeleri
  • Arayüzler (Interface)
  • Kütüphaneler
  • Sözleşmeler

Arayüzler, kütüphaneler veya sözleşmeler içinde sıra şu şekilde olmalıdır -

  • Tip bildirimleri
  • Durum (state) değişkenleri
  • Olaylar
  • Fonksiyonlar

Adlandırma kuralları

  • Sözleşme ve Kitaplık, CapWords Stili kullanılarak adlandırılmalıdır. Örneğin, SmartContract, Owner vb.
  • Sözleşme ve kütüphane adı, dosya adlarıyla eşleşmelidir.
  • Bir dosyada birden fazla sözleşme/kütüphane olması durumunda, temel sözleşme/kütüphane adını kullanın.

Struct İsimleri − SmartCoin gibi CapWords Stili kullanın.
Events Adları − Para Yatırma, AfterTransfer gibi CapWords Stilini kullanın.
Fonksiyon Adları − initialSupply gibi karışıkCase Stili kullanın.
Local ve State değişkenleri− CreatorAddress, arz gibi karışıkCase Stili kullanın.
Sabitler − MAX_BLOCKS gibi sözcükleri ayırmak için tüm büyük harfleri alt çizgi ile kullanın.
Modifier Adları − SadeceAfter gibi mixCase Stili kullanın.
Enum Adları − TokenGroup gibi CapWords Stili kullanın.

Fonksiyonlar

Fonksiyon , programınızdaki herhangi bir yerde çağrılabilen yeniden kullanılabilir bir kod grubudur. Bu, aynı kodu tekrar tekrar yazma ihtiyacını ortadan kaldırır. Programcıların modüler kodlar yazmalarına yardımcı olur. Fonksiyonlar, bir programcının büyük bir programı bir dizi küçük ve yönetilebilir fonksiyona ayırmasına izin verir.

Diğer gelişmiş programlama dilleri gibi Solidity de fonksiyonları kullanarak modüler kod yazmak için gerekli tüm özellikleri destekler. Bu bölüm, Solidity’de kendi fonksiyonlarınızı nasıl yazacağınızı açıklar.

Fonksiyon Tanımı

Bir fonksiyonu kullanmadan önce onu tanımlamamız gerekir. Solidity’de bir fonksiyonu tanımlamanın en yaygın yolu, function anahtar sözcüğünü, ardından benzersiz bir işlev adı, bir parametre listesi (boş olabilir) ve küme parantezleriyle çevrili bir ifade bloğu kullanmaktır.

Yazım şekli

function function-name(parametre-listesi) scope returns() {
//statements
}

Örnek

pragma solidity ^0.8.7;

contract Test {
function getResult() public view returns(uint){
uint a = 1; // local variable
uint b = 2;
uint result = a + b;
return result;
}
}

Fonksiyonu Çağırma

Sözleşmede daha sonra bir yerde bir işlevi çağırmak için, aşağıdaki kodda gösterildiği gibi o işlevin adını yazmanız yeterlidir.

Solidity’de kodun nasıl çalıştığını anlamak için aşağıdaki kodu Remix IDE üzerinden deneyin.

pragma solidity ^0.8.7;

contract SolidityTest {
constructor() public{
}
function getResult() public view returns(string memory){
uint a = 1;
uint b = 2;
uint result = a + b;
return integerToString(result);
}
function integerToString(uint _i) internal pure
returns (string memory) {

if (_i == 0) {
return "0";
}
uint j = _i;
uint len;

while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len - 1;

while (_i != 0) {
bstr[k--] = byte(uint8(48 + _i % 10));
_i /= 10;
}
return string(bstr);//access local variable
}
}

Çıktı:

0: string: 3

Yukarıdaki örnekte integerToString i çağırırken parametre ilettik. Geçirilen bu tür parametreler, fonksiyon içinde yakalanabilir ve bu parametreler üzerinde herhangi bir değişiklik yapılabilir. Bir fonksiyon, virgülle ayrılmış birden çok parametre alabilir.

Bir Solidity fonksyionu, isteğe bağlı bir return ifadesine sahip olabilir. Bir fonksiyondan bir değer döndürmek istiyorsanız bu gereklidir.

Fonksiyon Değiştiriciler (Modifiers)

Fonksyion değiştiriciler, bir işlevin davranışını değiştirmek için kullanılır. Örneğin, bir işleve ön koşul eklemek için.

İlk önce parametreli veya parametresiz bir değiştirici oluşturuyoruz.

contract Owner {
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier costs(uint price) {
if (msg.value >= price) {
_;
}
}
}

Fonksyion gövdesi, “_;” özel sembolünün bulunduğu yere eklenir. bir modifier tanımında görünür. Bu nedenle, bu işlev çağrılırken değiştirici koşulu karşılanırsa, işlev yürütülür ve aksi takdirde bir istisna atılır.

pragma solidity ^0.5.0;

contract Owner {
address owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
modifier costs(uint price) {
if (msg.value >= price) {
_;
}
}
}
contract Register is Owner {
mapping (address => bool) registeredAddresses;
uint price;
constructor(uint initialPrice) public { price = initialPrice; }

function register() public payable costs(price) {
registeredAddresses[msg.sender] = true;
}
function changePrice(uint _price) public onlyOwner {
price = _price;
}
}

Yukarıdaki örnekte eğer chanPrice fonksiyonunu çağıran kişi gönderen kişi ise modifier devreye girer ve price ı sahibin değiştirmesine izin verir.

Görünüm (View) Fonksiyonları

Görünüm fonksiyonları, durumu değiştirmemelerini sağlar. Bir fonksiyon görünüm olarak bildirilebilir. Aşağıdaki ifadeler, eğer fonksiyonda mevcutsa, durumu değiştirmek olarak kabul edilir ve bu gibi durumlarda derleyici uyarı verir.

Daha fazlası için: https://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=view%20function#view-functions

  • Durum (state)değişkenlerini değiştirme.
  • Olayları yaymak (Emitting Events).
  • Diğer sözleşmelerin oluşturulması.
  • Kendi kendini imha etme.
  • Çağrılar yoluyla Ether gönderme.
  • Görünüm veya saf olarak işaretlenmemiş herhangi bir işlevi çağırmak.
  • Düşük seviyeli aramaları kullanma.
  • Belirli işlem kodlarını içeren satır içi derlemeyi kullanma.
  • Getter yöntemi varsayılan olarak görünüm işlevleridir.

Örnek

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;

contract C {
function f(uint a, uint b) public view returns (uint) {
return a * (b + 42) + block.timestamp;
}
}

Çıktı

0: uint256: 1645530461

Pure Fonksiyonlar

Pure fonksiyonlar , durumu okumamalarını veya değiştirmemelerini sağlar. Bir fonksiyon pure olarak bildirilebilir. Aşağıdaki ifadeler eğer fonksiyonda mevcutsa durumu okuyor olarak kabul edilir ve bu gibi durumlarda derleyici uyarı verir.

  • Durum değişkenlerini okuma.
  • address(this).balance veya <address>.balance’e erişebilme.
  • Block, tx, msg gibi özel değişkenlerden herhangi birine erişim (msg.sig ve msg.data okunabilir).
  • Pure olarak işaretlenmemiş herhangi bir işlevi çağırmak.
  • Belirli işlem kodlarını içeren satır içi derleme kullanma.
  • Saf işlevler, bir hata oluştuğunda olası durum değişikliklerini geri almak için revert() ve require() işlevlerini kullanabilir.

Örnek:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;

contract C {
function f(uint a, uint b) public pure returns (uint) {
return a * (b + 42);
}
}

Çıktı:

0: uint256: 46

Fallback Fonksiyon

Fallback fonksiyonu, bir sözleşme için kullanılabilen özel bir işlevdir.

Aşağıdaki özelliklere sahiptir.

  • Sözleşmede var olmayan bir işlev çağrıldığında çağrılır.
  • Harici olarak işaretlenmesi gerekmektedir.
  • Adı yok.
  • Hiçbir argümanı yok
  • Hiçbir şeyi return edemez.
  • Sözleşme başına bir tane tanımlanabilir.
  • Ödenebilir olarak işaretlenmezse, sözleşme veri olmadan düz eter alırsa istisna atar.

Örnek:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.9.0;

contract Test {
uint x;
// This function is called for all messages sent to
// this contract (there is no other function).
// Sending Ether to this contract will cause an exception,
// because the fallback function does not have the `payable`
// modifier.
fallback() external { x = 1; }
}

contract TestPayable {
uint x;
uint y;
// This function is called for all messages sent to
// this contract, except plain Ether transfers
// (there is no other function except the receive function).
// Any call with non-empty calldata to this contract will execute
// the fallback function (even if Ether is sent along with the call).
fallback() external payable { x = 1; y = msg.value; }

// This function is called for plain Ether transfers, i.e.
// for every call with empty calldata.
receive() external payable { x = 2; y = msg.value; }
}

contract Caller {
function callTest(Test test) public returns (bool) {
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1.

// address(test) will not allow to call ``send`` directly, since ``test`` has no payable
// fallback function.
// It has to be converted to the ``address payable`` type to even allow calling ``send`` on it.
address payable testPayable = payable(address(test));

// If someone sends Ether to that contract,
// the transfer will fail, i.e. this returns false here.
return testPayable.send(2 ether);
}

function callTestPayable(TestPayable test) public returns (bool) {
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1 and test.y becoming 0.
(success,) = address(test).call{value: 1}(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1 and test.y becoming 1.

// If someone sends Ether to that contract, the receive function in TestPayable will be called.
// Since that function writes to storage, it takes more gas than is available with a
// simple ``send`` or ``transfer``. Because of that, we have to use a low-level call.
(success,) = address(test).call{value: 2 ether}("");
require(success);
// results in test.x becoming == 2 and test.y becoming 2 ether.

return true;
}
}

Fonksiyon Overloading

Aynı kapsamda aynı fonksiyon adı için birden fazla tanımlamanız olabilir. Fonksiyonun tanımı, argüman listesindeki argüman tiplerine ve/veya sayısına göre birbirinden farklı olmalıdır. Yalnızca dönüş türüne göre farklılık gösteren fonksiyon bildirimlerini aşırı yükleyemezsiniz.

Örnek

pragma solidity ^0.5.0;

contract Test {
function getSum(uint a, uint b) public pure returns(uint){
return a + b;
}
function getSum(uint a, uint b, uint c) public pure returns(uint){
return a + b + c;
}
function callSumWithTwoArguments() public pure returns(uint){
return getSum(1,2);
}
function callSumWithThreeArguments() public pure returns(uint){
return getSum(1,2,3);
}
}

Yukarıdaki örnekte callSumWithThreeArguments ve callSumWithTwoArguments çağırdığınızda aynı adı taşısada getSum farklı değerler döndürecektir.

Çıktı

0: uint256: 3
0: uint256: 6

Matematik Fonksiyonları

Solidity, yerleşik matematiksel fonksiyonlar sağlar. Aşağıdakiler yoğun olarak kullanılan yöntemlerdir.

addmod(uint x, uint y, uint k) cevap olarak (uint) değer döndürür− (x + y) % k değerini hesaplar ve değeri döndürür.

mulmod(uint x, uint y, uint k) cevap olarak (uint) değer döndürür − (x * y) % k değerini hesaplar

Örnek

pragma solidity ^0.5.0;

contract Test {
function callAddMod() public pure returns(uint){
return addmod(4, 5, 3);
}
function callMulMod() public pure returns(uint){
return mulmod(4, 5, 3);
}
}

Önce callAddMod sonra callMulMod ları çağırdığınızda aşağıdaki çıktıları almanız gerekir.

Çıktı

0: uint256: 0
0: uint256: 2

Kriptografik Fonksiyonlar

Solidity, yerleşik şifreleme fonksiyonları sağlar. Aşağıdakiler önemli yöntemlerdir -

  • keccak256(bytes memory) fonksiyonu (bytes32) döndürür — girdinin Keccak-256 hash ini hesaplar.
  • sha256(bytes memory) fonksiyonu (bytes32) döndürür — girdinin SHA-256 hash ini hesaplar.
  • ripmd160(bytes memory) fonksiyonu (bytes20) döndürür — girdinin RIPEMD-160 hash ini hesaplar.
  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) fonksiyonu (adres) döndürür − ortak anahtarla ilişkili adresi eliptik eğri imzasından ( elliptic curve signature) kurtarır veya hata durumunda sıfır döndürür. Fonksiyon parametreleri, imzanın ECDSA değerlerine karşılık gelir: r — ilk 32 byte imza; s: ikinci 32 byte imza; v: son 1 byte imza. Bu yöntem bir adres döndürür.

Örnek

pragma solidity ^0.5.0;

contract Test {
function callKeccak256() public pure returns(bytes32 result){
return keccak256("ABC");
}
}

Çıktı

0: bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8

Withdrawal (Geri Çekme) Modeli

Withdrawal modeli, güvenlik tehdidi oluşturan doğrudan transfer aramasının yapılmamasını sağlar. Aşağıdaki sözleşme, ether göndermek için transfer çağrısının güvensiz kullanımını gösteriyor.

pragma solidity ^0.5.0;

contract Test {
address payable public richest;
uint public mostSent;

constructor() public payable {
richest = msg.sender;
mostSent = msg.value;
}
function becomeRichest() public payable returns (bool) {
if (msg.value > mostSent) {
// Insecure practice
richest.transfer(msg.value);
richest = msg.sender;
mostSent = msg.value;
return true;
} else {
return false;
}
}
}

Yukarıdaki sözleşme, en zenginin başarısız bir geri dönüş fonksiyon sözleşmesi olmasına neden olarak kullanılamaz duruma getirilebilir. Geri dönüş işlevi başarısız olduğunda, becomeRichest() işlevi de başarısız olur ve sözleşme sonsuza kadar takılı kalır. Bu sorunu azaltmak için Geri Çekme Modelini kullanabiliriz.

Para çekme modelinde, her transferden önce bekleyen tutarı sıfırlayacağız. Yalnızca arayan sözleşmesinin başarısız olmasını sağlayacaktır.

Örnek

pragma solidity ^0.5.0;

contract Test {
address public richest;
uint public mostSent;

mapping (address => uint) pendingWithdrawals;

constructor() public payable {
richest = msg.sender;
mostSent = msg.value;
}
function becomeRichest() public payable returns (bool) {
if (msg.value > mostSent) {
pendingWithdrawals[richest] += msg.value;
richest = msg.sender;
mostSent = msg.value;
return true;
} else {
return false;
}
}
function withdraw() public {
uint amount = pendingWithdrawals[msg.sender];
pendingWithdrawals[msg.sender] = 0;
msg.sender.transfer(amount);
}
}

Kısıtlı Erişim

Bir Sözleşmeye Kısıtlı Erişim yaygın bir uygulamadır. Varsayılan olarak, bir sözleşme durumu, genel olarak belirtilmediği sürece salt okunurdur.

Değiştiricileri kullanarak sözleşmenin durumunu kimlerin değiştirebileceğini veya bir sözleşmenin işlevlerini çağırabileceğini kısıtlayabiliriz. Aşağıda açıklandığı gibi birden fazla değiştirici oluşturacağız ve kullanacağız -

  • onlyBy — bir fonksiyonda bir kez kullanıldığında, yalnızca belirtilen kişi bu fonksiyonu çağırabilir.
  • onlyAfter — bir fonksiyonda bir kez kullanıldığında, o fonksiyon belirli bir süre sonra çağrılabilir.
  • costs — bir fonksiyon kullanıldığında, arayan kişi bu işlevi yalnızca belirli bir değer sağlanmışsa çağırabilir.

Örnek

pragma solidity ^0.5.0;

contract Test {
address public owner = msg.sender;
uint public creationTime = now;

modifier onlyBy(address _account) {
require(
msg.sender == _account,
"Sender not authorized."
);
_;
}
function changeOwner(address _newOwner) public onlyBy(owner) {
owner = _newOwner;
}
modifier onlyAfter(uint _time) {
require(
now >= _time,
"Function called too early."
);
_;
}
function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
delete owner;
}
modifier costs(uint _amount) {
require(
msg.value >= _amount,
"Not enough Ether provided."
);
_;
if (msg.value > _amount)
msg.sender.transfer(msg.value - _amount);
}
function forceOwnerChange(address _newOwner) public payable costs(200 ether) {
owner = _newOwner;
if (uint(owner) & 0 == 1) return;
}
}

Kontratlar (Contracts)

Solidity’deki Sözleşme, C++’daki bir sınıfa benzer. Bir Sözleşme aşağıdaki özelliklere sahiptir.

  • Constructor — Her sözleşme için bir kez yürütülecek ve bir sözleşme oluşturulduğunda çağrılacak olan, constructor anahtar sözcüğüyle bildirilen özel bir fonksiyondur.
  • State Variables — Sözleşmenin durumunu saklamak için sözleşme başına tanımlanan değişkenlerdir.
  • Fonksiyonlar — Bir sözleşmenin durumunu değiştirmek için durum değişkenlerini değiştirebilen sözleşme başına fonksiyonlar.

Görünürlük Niceleyicileri
Aşağıda, bir sözleşmenin işlevleri/durum değişkenleri için çeşitli görünürlük niceleyicileri bulunmaktadır.

  • external − Harici işlevlerin diğer sözleşmeler tarafından çağrılması amaçlanmıştır. Dahili arama için kullanılamazlar. Sözleşme dahilinde harici işlevi çağırmak için this.function_name() çağrısı gereklidir. Durum değişkenleri harici olarak işaretlenemez.
  • public − Genel fonksiyonlar/değişkenler hem harici hem de dahili olarak kullanılabilir. Genel durum değişkeni için, Solidity otomatik olarak bir alıcı fonksiyonu oluşturur.
  • internal − Dahili fonksiyonlar/değişkenler yalnızca dahili olarak veya türetilmiş sözleşmelerle kullanılabilir.
  • private − Özel fonksiyonlar/değişkenler yalnızca dahili olarak kullanılabilir ve türetilmiş sözleşmelerle bile kullanılamaz.

Örnek

pragma solidity ^0.5.0;

contract C {
//private state variable
uint private data;

//public state variable
uint public info;

//constructor
constructor() public {
info = 10;
}
//private function
function increment(uint a) private pure returns(uint) { return a + 1; }

//public function
function updateData(uint a) public { data = a; }
function getData() public view returns(uint) { return data; }
function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//External Contract
contract D {
function readData() public returns(uint) {
C c = new C();
c.updateData(7);
return c.getData();
}
}
//Derived Contract
contract E is C {
uint private result;
C private c;

constructor() public {
c = new C();
}
function getComputedResult() public {
result = compute(3, 5);
}
function getResult() public view returns(uint) { return result; }
function getData() public view returns(uint) { return c.info(); }
}

Çeşitli Sözleşme yöntemlerini çalıştırın. E.getComputedResult() ve ardından E.getResult() için çıktı aşağıdaki gibi olacaktır

0: uint256: 8

Kalıtım (Inheritance)

Kalıtım (Inheritance), bir sözleşmenin işlevselliğini genişletmenin bir yoludur. Solidity, hem tekli hem de çoklu kalıtımı destekler. Aşağıda önemli noktalar bulunmaktadır.

  • Türetilmiş bir sözleşme, dahili (internal) yöntemler ve durum(state) değişkenleri dahil olmak üzere tüm özel olmayan üyelere erişebilir. Ama bunu kullanmak yasaktır.
  • Fonksiyon imzasının aynı kalması koşuluyla fonksiyonu geçersiz kılmaya izin verilir. Çıkış parametrelerinin farklı olması durumunda derleme başarısız olur.
  • Bir süper sözleşmenin işlevini süper anahtar kelimeyi veya süper sözleşme adını kullanarak çağırabiliriz.
  • Çoklu kalıtım durumunda, super kullanan işlev çağrısı, türetilmiş sözleşmelerin çoğuna öncelik verir.

Örnek

pragma solidity ^0.5.0;

contract C {
//private state variable
uint private data;

//public state variable
uint public info;

//constructor
constructor() public {
info = 10;
}
//private function
function increment(uint a) private pure returns(uint) { return a + 1; }

//public function
function updateData(uint a) public { data = a; }
function getData() public view returns(uint) { return data; }
function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//Derived Contract
contract E is C {
uint private result;
C private c;
constructor() public {
c = new C();
}
function getComputedResult() public {
result = compute(3, 5);
}
function getResult() public view returns(uint) { return result; }
function getData() public view returns(uint) { return c.info(); }
}

Çeşitli Sözleşme yöntemlerini çalıştırın. E.getComputedResult() ve ardından E.getResult() için şu çıktı gelir

Çıktı

0: uint256: 8

Kurucular (Constructors)

Constructor, constructor anahtar sözcüğü kullanılarak bildirilen özel bir fonksiyondur. İsteğe bağlı bir fonksiyondur ve bir sözleşmenin durum değişkenlerini başlatmak için kullanılır. Bir constructor un temel özellikleri aşağıdadır.

  • Bir sözleşmenin yalnızca bir constructor u olabilir.
  • Bir sözleşme oluşturulduğunda bir constructor kodu yürütülür ve sözleşme durumunu başlatmak için kullanılır.
  • Bir constructor kodu yürütüldükten sonra, son kod blok zincirine dağıtılır. Bu kod, genel fonksiyonları ve genel fonksiyonlar aracılığıyla erişilebilen kodu içerir. Oluşturucu kodu veya yalnızca constructor tarafından kullanılan herhangi bir dahili yöntem, nihai koda dahil edilmez.
  • Bir kurucu genel (public) veya dahili (internal) olabilir.
  • Dahili bir kurucu sözleşmeyi abstact olarak işaretler.
  • Herhangi bir constructor tanımlanmaması durumunda, sözleşmede varsayılan bir constructor bulunur.
pragma solidity ^0.5.0;

contract Test {
constructor() public {}
}
  • Temel sözleşmenin argümanları olan constructor ı olması durumunda, türetilmiş her sözleşmenin bunları iletmesi gerekir.
  • Temel kurucu, aşağıdaki yol kullanılarak doğrudan başlatılabilir.
pragma solidity ^0.5.0;

contract Base {
uint data;
constructor(uint _data) public {
data = _data;
}
}
contract Derived is Base (5) {
constructor() public {}
}
  • Temel kurucu, aşağıdaki yol kullanılarak dolaylı olarak başlatılabilir.
pragma solidity ^0.5.0;

contract Base {
uint data;
constructor(uint _data) public {
data = _data;
}
}
contract Derived is Base {
constructor(uint _info) Base(_info * _info) public {}
}
  • Temel sözleşme yapıcısını başlatmanın doğrudan ve dolaylı yollarına izin verilmez.
  • Türetilmiş sözleşme, temel sözleşme kurucusuna argüman(lar) iletmiyorsa, türetilmiş sözleşme abstract hale gelecektir.

Daha fazlası için: https://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=view%20function#constructors

Soyut (Abstract) Kontratlar

Abstract sözleşme, herhangi bir uygulaması olmayan en az bir fonksiyon içeren sözleşmedir. Böyle bir sözleşme, temel sözleşme olarak kullanılır. Genellikle soyut bir sözleşme, hem uygulanan hem de soyut işlevleri içerir. Türetilmiş (Derived) sözleşme, soyut işlevi uygulayacak ve gerektiğinde mevcut işlevleri kullanacaktır.

Daha fazla bilgi için: https://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=view%20function#abstract-contracts

Türetilmiş bir sözleşmenin abstract fonksiyonu uygulamaması durumunda, bu türetilmiş sözleşme özet olarak işaretlenecektir.

Örnek

pragma solidity ^0.5.0;

contract Calculator {
function getResult() public view returns(uint);
}
contract Test is Calculator {
function getResult() public view returns(uint) {
uint a = 1;
uint b = 2;
uint result = a + b;
return result;
}
}

Çıktı

0: uint256: 3

Arayüzler (Interfaces)

Arayüzler soyut sözleşmelere benzer ve interface anahtar sözcüğü kullanılarak oluşturulur. Bir arayüzün temel özellikleri aşağıdadır.

  • Arayüz, uygulama ile herhangi bir fonksiyona sahip olamaz.
  • Bir arabirimin fonksiyonları yalnızca harici (external) türde olabilir.
  • Arayüz constructor sahip olamaz.
  • Arabirim durum değişkenlerine sahip olamaz.
  • Arayüz, arayüz adı nokta gösterimi kullanılarak erişilebilen enum, yapılara sahip olabilir.

Örnek

pragma solidity ^0.5.0;

interface Calculator {
function getResult() external view returns(uint);
}
contract Test is Calculator {
constructor() public {}
function getResult() external view returns(uint){
uint a = 1;
uint b = 2;
uint result = a + b;
return result;
}
}

Çıktı

0: uint256: 3

Daha fazla bilgi için: https://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=view%20function#interfaces

Kütüphaneler (Libraries)

Kütüphaneler sözleşmelere benzer, ancak esas olarak yeniden kullanım amaçlıdır. Bir kütüphane, diğer sözleşmelerin çağırabileceği işlevleri içerir. Solidity, bir kütüphanenin kullanımıyla ilgili belirli kısıtlamalara sahiptir. Bir solidity kütüphanelerinin temel özellikleri aşağıdadır.

  • Durumu değiştirmezlerse, kütüphane fonksiyonları doğrudan çağrılabilir. Bu, pure veya view fonksiyonlarının yalnızca kütüphanenin dışından çağrılabileceği anlamına gelir.
  • Kütüphane yok edilemez.
  • Bir kütüphanenin durum(state) değişkenleri olamaz.
  • Bir kütüphane herhangi bir öğeyi devralamaz.
  • Bir kütüphane devralınamaz.

Daha fazla bilgi için: https://docs.soliditylang.org/en/v0.8.12/contracts.html?highlight=view%20function#libraries

Örnek

pragma solidity ^0.5.0;

library Search {
function indexOf(uint[] storage self, uint value) public view returns (uint) {
for (uint i = 0; i < self.length; i++) if (self[i] == value) return i;
return uint(-1);
}
}
contract Test {
uint[] data;
constructor() public {
data.push(1);
data.push(2);
data.push(3);
data.push(4);
data.push(5);
}
function isValuePresent() external view returns(uint){
uint value = 4;

//search if value is present in the array using Library function
uint index = Search.indexOf(data, value);
return index;
}
}

Çıktı

0: uint256: 3

Assembly Kullanımı

Solidity, Solidity kaynak kodu içinde satır içi derleme yazmak için assembly dilini kullanma seçeneği sunar. Daha sonra byte koduna dönüştürülecek bağımsız bir derleme kodu da yazabiliriz. Bağımsız Derleme, bir Solidity derleyicisi için bir ara dildir ve Solidity kodunu Bağımsız Bir assembly ve ardından byte koduna dönüştürür. Bağımsız bir derlemede kod yazmak için Satır İçi assembly kullanılan dili kullanabiliriz.

Inline Assembly
EVM üzerinde daha fazla hassas kontrole sahip olmak için satır içi aseembly kodu, Solidity kod tabanı içinde serpiştirilebilir ve özellikle kütüphane fonksiyonları yazılırken kullanılır.

Assembly { … } bloğunun altına bir Assembly kodu yazılır.

Örnek

pragma solidity ^0.5.0;

library Sum {
function sumUsingInlineAssembly(uint[] memory _data) public pure returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i) {
assembly {
o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
}
}
}
}
contract Test {
uint[] data;

constructor() public {
data.push(1);
data.push(2);
data.push(3);
data.push(4);
data.push(5);
}
function sum() external view returns(uint){
return Sum.sumUsingInlineAssembly(data);
}
}

Olaylar (Events)

Event, bir sözleşmenin kalıtsal bir üyesidir. Bir olay yayınlanır, işlem günlüklerinde iletilen argümanları depolar. Bu günlükler blok zincirinde saklanır ve sözleşme blok zincirinde mevcut olana kadar sözleşmenin adresi kullanılarak erişilebilir. Oluşturulan bir olaya, onları yaratan ve yayan bile, sözleşmeler içinden erişilemez.

// Event bildir
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);

// Event yayınla
emit Deposit(msg.sender, _id, msg.value);

Hata Yakalama — İşleme

Solidity, hata işleme için çeşitli fonksiyonlar sağlar. Genellikle bir hata oluştuğunda, durum orijinal durumuna geri döndürülür. Diğer kontroller yetkisiz kod erişimini önlemek içindir. Hata işlemede kullanılan önemli yöntemlerden bazıları aşağıdadır

  • assert(bool koşulu) − Koşulun sağlanmaması durumunda, bu yöntem çağrısı geçersiz bir işlem koduna neden olur ve durumda yapılan tüm değişiklikler geri alınır. Bu yöntem dahili hatalar için kullanılacaktır.
  • require(bool koşulu) − Koşulun sağlanmaması durumunda, bu yöntem çağrısı orijinal durumuna geri döner. — Bu yöntem, girişlerdeki veya harici bileşenlerdeki hatalar için kullanılacaktır.
  • require(bool koşulu, dize bellek mesajı) − Koşulun karşılanmaması durumunda, bu yöntem çağrısı orijinal durumuna geri döner. — Bu yöntem, girişlerdeki veya harici bileşenlerdeki hatalar için kullanılacaktır. Özel bir mesaj verme seçeneği sunar.
  • revert() − Bu yöntem yürütmeyi durdurur ve durumda yapılan tüm değişiklikleri geri alır.
  • revert(dize bellek nedeni) − Bu yöntem yürütmeyi durdurur ve durumda yapılan tüm değişiklikleri geri alır. Özel bir mesaj sağlama seçeneği sunar.

Örnek

pragma solidity ^0.5.0;

contract Vendor {
address public seller;
modifier onlySeller() {
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
function sell(uint amount) public payable onlySeller {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
// Perform the sell operation.
}
}

Çıktı

0x08c379a0     // Function selector for Error(string)
0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset
0x000000000000000000000000000000000000000000000000000000000000001a // String length
0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data

Kapanış

Açıkcası bu kadar uzun bir yazı olacağını ilk başta düşünmemiştim. Fakat kullandığım kaynak bu kadar detaya girince ben de tamamlayana kadar devam ettim. Solidity dışında yazılımın temelinde yer alan hemen her şeyle ilgili bir kaç kelam etme fırsatı oldu. Yazının tamamını vakit ayırıp okuduğunuz durumda ve uygulamada bir kaç kullanımdan sonra artık neyi nerede nasıl kullanmanız gerektiğine de hakim olabileceğinize inanıyorum.

Kaynaklar:

Beni Youtube, Twitter ve LinkedIn’de takip edebilirsiniz.

--

--