Akıllı Sözleşmelerde Değer Türleri #2

Akıllı Sözleşmelerde Değer Türleri #2

Ethereum akıllı sözleşme türleri, sabit ve değişken büyüklükteki byte listeleri, adres sabitleri(address literals), rasyonel sayı, tam sayı ve string sabitleri(rational, integer, string literals), listeler(enums) ve kullanıcı tanımlı değer türlerini incelyeceğimiz Solidity Sözleşmelerinde Değer Türleri #2 yazımıza hazırsanız başlayalım.

Başlamadan Önce

Yazımıza başlamadan önce bilmemizin faydalı olacağı birkaç kavramı burada listeledim.

Unicode Nedir?

Unicode (Evrensel Kod) Unicode Consortium organizasyonu tarafından geliştirilen ve her karaktere bir sayı değeri karşılığı atayan bir endüstri standardıdır.[ayr. bkz.]

Hexadecimal Nedir?

Bilişim ve matematik alanında kullanılan 1-9 rakamlarının 1-9 sembolleriyle, 10-16 sayılarının sırasıyla A-F arasındaki harflerle temsil edildiği on altılık tabanlı sayı sisteminin adıdır. Benzer olarak ikili sayı sistemi ve onlu sayı sistemi de vardır.

UTF8 Nedir?

UTF-8, UTF-16 ise birer karakter kodlamasıdır. Açılımı, Unicode Transformation Format olan UTF 8, 8-bitlik byte grubları oluşturmayı sağlayan karakterlerin kodlamasıdır ve Rob Pike ve Ken Thompson tarafından geliştirilmiştir.

ASCII ve Unicode Nedir?

ASCII ve Unicode birer karakter setidir. ASCII (American Standard Code for Information Interchange) 7 bitlik karakter kod setidir. 0 ve 127'de dahil olmak üzere aralarındaki tüm sayıların binary karşılıklarının belirlendiği bir karakter setidir.

Unicode ise ASCII karakterlerinin yetersiz gelmesiyle birlikte oluşturulan karakter setidir. ASCII'deki 7 bitlik karakter tanımlamasına kıyasla kalan karakterlerin(semboller, farklı dillerdeki karakterler vb.) 8-bitlik karakterleri kullanılarak oluşturulduğu bir karakter setidir.

Sözleşme Türleri

EN: Contract Types

Her sözleşme kendi türünü tanımladığı için başka bir sözleşmeyi kalıtabilir.

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


//ContractTypes isimli bir sözleşmemiz
contract ContractTypes{


}


//ContractTypes2 isimli sözleşmemizi ContractTypes isimli diğer sözleşmemizden kalıttık.
/*contract <Kendi Sözleşmemizin İsmi> is <Kalıtacağımız Ana Sözleşme İsmi>*/
contract  ContractTypes2 is ContractTypes{
    

Sözleşmeler, to ve from adres türlerine dönüştürülebilir. address payable kodu bir sözleşmedeki fonksiyonların payable olmasıyla mümkünken bunun haricinde payable bir address üretmek payable(<address>) ile mümkün olmaktadır. Aşağıdaki örneğe göz atabilirsiniz.

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


contract yonetimSozlesmesi{
    //Yapılandırıcı
    constructor() {
        //yonetici değişkeni sözleşmeyi oluşturan kişinin adresidir. (msg.sender) Bu adresin ödenebilir özelliğine sahip olması için
        // payable(msg.sender) fonksiyonunu çağırdık. Çağırmadığımızda bir hata meydana gelecektir.
        yonetici = payable(msg.sender);
    }
    //Tanımladığımız ödenebilir adres türündeki yonetici değişkeni
    address payable yonetici;
}
Solidity 0.5.0 versiyonundan önce address ve address payable arasında fark gözetilmezdi. Tek bir addresskodu ile türetilirdi.

Sözleşmeler kendi içlerinde de tekrardan kalıtılabilir. Kalıtılan sözleşmedeki fonksiyonlar çağrılarak çeşitli işlemler gerçekleştirilebilir ve değerler alınarak atanabilir.

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


contract YonetimSozlesmesi{
    //Yapılandırıcı
    constructor() {
        //yonetici değişkeni sözleşmeyi oluşturan kişinin adresidir. (msg.sender) Bu adresin ödenebilir özelliğine sahip olması için
        // payable(msg.sender) fonksiyonunu çağırdık. Çağırmadığımızda bir hata meydana gelecektir.
        yonetici = payable(msg.sender);
    }
    address payable yonetici;


    function kalitimYapacagimizFonksiyon() public view returns(address payable){
        return yonetici;
    }


    //Aynı Sozlesmeyi kalıttık.
    YonetimSozlesmesi sozlesme;
    //Kalıtmış olduğumuz sözleşmeden adres değerini fonksiyon yardımıyla çektik.
    address payable yoneticiKalitilmis = sozlesme.kalitimYapacagimizFonksiyon();

Sözleşmelerde herhangi bir operatör kullanılmaz. Ayrıca ana sözleşmenin kopyalarındaki(members of a contract) herhangi bir fonksiyonun kullanılması için fonksiyonun public türünde olması gerekmektedir.

Not olarak, fonksiyonlara eklenecek public, returns(), payable gibi türler fonksiyon isminden sonra tanımlanmalıdır.

    //public türü fonksiyon isminden önce tanımlandığı için hata fırlatılıyor.
    function public kalitimYapacagimizFonksiyon() view returns(address payable){
        return yonetici;
    }


    //function <-Fonksiyon İsminiz-> <-Tür Etiketleri(public, view vb)-> şeklinde kullanım doğrudur.
    function kalitimYapacagimizFonksiyon() public view returns(address payable){
        return yonetici;
    }

Sözleşmemizin bazı özelliklerine type()fonksiyonu ile erişebiliriz. Örneğin sözleşme ismi, type(C).name, type(C).creationCode, type(C).runtimeCode, type(T).min, type(T).max, type(I).interfaceId gibi değerlere erişebiliriz. [ayr. bkz.] Bu konuyu bu yazımızda örnekleyip geçeceğim. İlerleyen yazılarda daha ayrıntılı inceleyebiliriz.

Sabit Büyüklükteki Byte Listeleri

EN: Fixed-size byte arrays

Sabit büyüklükteki veri türleri bytes1 , bytes2 bytes3 ... bytes32 yani X, 1-32 aralığında bir tam sayı olursa bytesX örneğindeki gibi herhangi bir şekilde tanımlanabilmektedir. Her rakam kaç byte verinin tutulacağını göstermektedir. İkili sayı sistemi alfabesine göre düşünürsek her bir rakam, harf veya sembol 8 bitlik bir byte değerine sahiptir. Yani bytes3 değişkeni 1-2-3 karakterli bir string değerine sahip olabilir ancak 3 karakterden fazla değer tanımlamamıza izin verilmez. Örnek olması açısından aşağıdaki kodu inceleyebiliriz.

    //maksimum 32 byte(1 byte = 8 bit) değerinde veri koyulabilir.
    bytes32 ornek = "ornekornekornekornekornekornekor";//maksimum 32 karakter veri depolayabilir.


    //maksimum 10 byte(1 byte = 8 bit) değerinde veri koyulabilir.
    bytes10 ornek2 = "ornek2orne";//maksimum 10 karakter veri depolayabilir.

Operatörler

Byte listeleri ile karşılaştırma, shift, bit ve sıralama operatörleri kullanılmaktadır.

  • Karşılaştırma operatörleri(comparisons), <=<==!=>=> (evaluate to bool)
  • Bit operatörleri, &|^ (bit seviyesinde özel veya), ~ (bit seviyesinde olumsuzlama)
  • Kaydırma Operatörleri(Shift Operators), << (sola kaydırma), >> (sağa kaydırma),
  • Sıralama operatörleri, a bytesX türünde bir değer ise a[i], 0 <= i < X şartını sağladığı taktirde okunabilir bir byte değeri döndürecektir.

.length, byte listelerinin(byte arrays), uzunluğunu uint değerinde döndürür.

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


contract BitwiseOperatorleri{
    //Örnek bitwise operatörleri kullanımı
    function f() public pure returns(bytes1[4] memory){
        bytes1 a = 0xb5; //  [10110101] byte
        bytes1 b = 0x56; //  [01010110] byte


        bytes1 sonucOr = a & b;
        bytes1 sonucAnd = a | b;
        bytes1 sonucXOr = a ^ b;
        bytes1 sonucOlumsuzlama =  ~b;


        //Tüm bytes1 türlerinden bir liste oluşturduk. Tüm elemanların listeye tanımlanmış türde olması gerekmekte.
        bytes1[4] memory ornekByteArray = [sonucOr,sonucAnd,sonucXOr,sonucOlumsuzlama];
        //Array uzunluğunu aldık. Uzunluk değeri uint türünde olmalıdır. Aksi taktirde hata fırlatacaktır.
        uint256 len = ornekByteArray.length;


        return ornekByteArray;
    

Değişken Büyüklükteki Byte Listeleri

EN: Dynamically-sized byte array

Değişken büyüklükteki byte listeleri bytes, değişken büyüklükteki metin listeleri ise string ile kullanılır.

stringbytes değişkenine eşittir ancak string 'de bytes'daki gibi byte sırasına [k] kullanarak erişemeyiz ve byte listemize belirli bir büyüklük veremeyiz. Büyüklük dinamik olarak ayarlanır!

Adres Değişmezleri

EN: Address Literals

Adres değişmezleri address ile tanımlanan, hexadecimal sayı sistemine göre oluşturulan ve kontrol edilen, 39 ve 41 hane arasındaki değişmez değerlerdir.

0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF örnek bir adrestir. [Kaynak: Solidity Docs.]

Karışık adresler tanımlanırken EIP-55 şartı benimsenir.

address ornekAdres = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF; //Kaynak: Solidity Docs

Rasyonel ve Tam Sayı Değişmezleri

Tam sayı değişmezleri, 0 ve 9 arasındaki rakamlardan meydana gelen sayılardır. Sekizli değişmezler Solidity'de desteklenmemektedir.

    //DOĞRU KULLANIMLAR
    uint128 sayiOrnek = 2.5 + 0.5; //Toplamı bir tam sayı olduğundan sorunsuz çalışır.
    uint128 sayiOrnek2 = 3;
    uint128 sayiOrnek3 = 3.0; 
    uint128 sayiOrnek4 = 5 / 2 + 2.5; //Toplamı bir tam sayı olduğundan sorunsuz çalışır.


    //HATALI KULLANIMLAR
    uint128 sayiHataliOrnek = 0.3;
    uint128 sayiHataliOrnek1 = 5.7;
    uint128 sayiHataliOrnek2 = 60 / 22;

Bilimsel sayı değerleri Solidity'de desteklenmektedir. Örneğin, A ve B iki sayı olsun(aynı da olabilir) AeB şeklinde bir sayı tanımlaması yaptığımızda sonuç A * 10 ** B yani A çarpı 10 üzeri B olacaktır.

    int bilimselGosterim = 2e10;//2 * 10 üzeri 10
    int bilimselGosterim2 = 2e1;//2 * 10 üzeri 1 = 20
    int bilimselGosterim3 = -3e2;//-3 * 10 üzeri 2 = -300

Solidity'de sayılar 0 ile başlamazlar. Örneğin 0.4 sayısını kullanmak istediğimiz zaman .4 şeklinde tanımlama yapmamız gerekmektedir.

    int ornekCarpim = .5 * 8; // 8 ile 0.5 çarpımı => 4

Sayı değişmezleri birbirleriyle bir işlemde kullanılacakları zaman işlemlerin sonuçlarının öncelikleri önemlidir. Örneğin uint128 türünde bir b değeri elde etmek için bazı işlemler yapıyor olalım. b değeri sonuç olarak tam sayı çıkıyor dahil olsa işlem önceliği kuralları uygulanmadığı için karşımıza bir hata çıkacaktır.

    //Değişmez bir a sayısı
    uint128 a = 1;
    // b sayısı aşağıdaki şekilde elde edilmek istendiğinde HATA fırlatır!
    uint128 b = 2.5 + a + 0.5; 
    //Benzer olarak bu şekilde kullanılabilir.
    uint128 bRevizeEdilmis = (2.5 + 0.5) + a;

2.5 + a bir ondalıklı sayı olduğu için ETH sanal makinesi bu kodu kabul etmeyecektir. Benzer olarak (2.5 + 0.5) + a bir kod kullanarak tanımladığımızda önce parantez içerisi toplandığı için çıkan sayı istenen türde yani uint olacaktır. Sonrasında yine aynı türdeki a ile topladığımızda bir sorun çıkmayacaktır.

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


contract DegismezlerLiteralsSozlesmeOrnegiSayilar {
    //DOĞRU KULLANIMLAR
    uint128 sayiOrnek = 2.5 + 0.5; //Toplamı bir tam sayı olduğundan sorunsuz çalışır.
    uint128 sayiOrnek2 = 3;
    uint128 sayiOrnek3 = 3.0; 
    uint128 sayiOrnek4 = 5 / 2 + 2.5; //Toplamı bir tam sayı olduğundan sorunsuz çalışır.


    //HATALI KULLANIMLAR
    // uint128 sayiHataliOrnek = 0.3;
    // uint128 sayiHataliOrnek1 = 5.7;
    // uint128 sayiHataliOrnek2 = 60 / 22;
    
    int bilimselGosterim = 2e10;//2 * 10 üzeri 10
    int bilimselGosterim2 = 2e1;//2 * 10 üzeri 1 = 20
    int bilimselGosterim3 = -3e2;//-3 * 10 üzeri 2 = -300


    int dagitikSayi1 = 589_000; //589000
    int dagitikSayi2 = 0x2efff_abde; // 12616444894
    int dagitikSayi3 = 589_20341; //58920341


    int ornekCarpim = .5 * 8; // 8 ile 0.5 çarpımı => 4


    //Değişmez bir a sayısı
    uint128 a = 1;
    // b sayısı aşağıdaki şekilde elde edilmek istendiğinde hata fırlatır.
    uint128 b = 2.5 + a + 0.5; 
    //Benzer olarak bu şekilde kullanılabilir.
    uint128 bRevizeEdilmis = (2.5 + 0.5) + a;


    //Sayıları almak için kullandığımız fonksiyon.
    function get() public view returns(int){
        return ornekCarpim;
   

Metin Değişmezleri

Metin değişmezleri(string literals), tek ya da çift tırnak içerisine yazılabilen karakterlerdir. String değişmezleri, yalnızca ASCII karakterlerini içerebilirler. String değerler arasında boşluk bırakılarak ayrı ayrı yazılabildiği gibi birbirlerine eklenerek de kullanılabilir. Örnek koda baktığımızda "solidity" "egitimi" ile "solidityegitimi" aynı değere eşittir.

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


contract DegismezlerLiteralsSozlesmeOrnegiString {


    string ciftTirnakString = "cift tirnak isareti";


    string tekTirnakString = 'tek tirnak isareti';


    string bosluklarlaAyrilmisString = "solidity" "egitimi"; //eşittir => "solidityegitimi

Ayrıca string değişmezleri, kaçış karakterlerini(escape characters) de desteklemektedir.

  • \<newline> (diğer satıra atlar)
  • \\ (ters slash)
  • \' (tek tırnak)
  • \" (çift tırnak)
  • \n (yeni satır açar ve oradan ilerler)
  • \r (carriage return)
  • \t (tab, 4 karakter boşluk bırakır)
  • \xNN (hex kaçış işareti)
  • \uNNNN (unicode kaçış işareti)

\xNN hex değeri alır ve byte ekler. \uNNNN Unicode değeri alır ve UTF-8 dizesi ekler.

Unicode Değişmezleri

EN: Unicode Literals

ASCII tarafından desteklenmeyen karakterlerin kullanılmasını sağlamak adına unicode ön eki kullanılarak unicode karakterlerinin okunması ve kullanılması sağlanır.

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


contract DegismezlerLiteralsSozlesmeOrnegi {
    //HATALI KULLANIM! Ö, Ş gibi Unicode karakterler desteklenmediği için hata alacağız.
    string unicodeStringDegeri = "Ömer";
    
    //DOĞRU KULLANIM! "unicode" ön ekini kullanarak UTF-8 formatındaki Unicode karakterleri sorunsuz kullanabiliriz.
    string unicodeStringDegeri = unicode"Ömer";


Hexadecimal Değişmezleri

EN: Hexadecimal Literals

Hexadecimal Değişmezleri, hex ön ekiyle kullanılır ve hex'BF' ya da hex"BF" şeklinde tek veya çoklu tırnak işaretleri ile kullanılabilr. Tırnakların içerisinde tanımlanacak olan değerler hexadecimal haneli olmalıdırlar yoksa sistem tarafından karşımıza hata fırlatılacaktır.

Hexadecimal değişmezleri _ ile byte sınırları arasında(byte boundaries) ayırıcı olarak kullanılabilir.

        bytes memory hexOrnek = hex'0011';
        bytes memory hexOrnek3 = hex'0011_FF';
        bytes memory hexOrnek4 = hex'0011_FF_0F';
        bytes memory hexOrnek5 = hex'0011_FF_0F_0F_0F';

Birden fazla hexadecimal değişmezler boşlukla ayrılabildiği gibi birbirlerine eklenerek de aynı değeri verecek şekilde kullanılabilirler.

hex"00112233" hex"44556677" = hex"0011223344556677"

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


contract DegismezlerLiteralsSozlesmeOrnegi {
    //Fonksyion içerisinden hexOrnegini aldık
    function getHex() public pure returns(bytes memory){
        bytes memory hexOrnek = hex"0F";
        return hexOrnek;
    }


    //Bir diğer kullanım
    bytes hexOrnek2 = hex"0F"; //Memory kodu hata veriyor.
    function getHexDifferent() public view returns(bytes memory){
        return hexOrnek2;
    }

Enums (Listeler)

Enums, Solidity'de kullanıcı tanımlı değer(user-defined type) oluşturmanın bir yoludur. Enum listeleri tanımlandıktan sonra kendisi dışındaki satırlarda değiştirilemez. Listeler en az bir eleman ile tanımlanabilir ve tanımlanan elemanlardan ilk eleman varsayılan elemandır.

Son olarak, listelerin maksimum ve minimum eleman sayısını type(ListeAdi).min ve type(ListeAdi).max ile alabiliriz. Ancak bu özellik her derleyici için geçerli değildir!

Listeler(Enums), 256 elemandan fazla elemana sahip olamaz.

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


contract EnumListeSozlesmeOrnegi {
    enum Aksiyonlar { SolaGit, SagaGit, GeriyeGit, OneGit }
    Aksiyonlar secim;
    Aksiyonlar constant varsayilanSecim = Aksiyonlar.GeriyeGit;


    function GeriyeGitmesiniSagla() public {
        secim = Aksiyonlar.GeriyeGit;
    }


    function secimiAl() public view returns (Aksiyonlar) {
        return secim;
    }


    function varsayilanSecimiAl() public pure returns (uint) {
        return uint(varsayilanSecim);
    }

Bu yazımızda akıllı sözleşmelerde değer türlerinin(value types) büyük çoğunluğunu tamamlamış olduk. Bir sonraki yazımızda fonksiyon türleri ve kullanıcı tanımlı değer türlerini detaylı şekilde inceleyeceğiz.

Yorumunu Bırak

Çok hızlısın. Biraz dinlendikten sonra tekrar devam edebilirsin.
Bugünlük gönderebileceğin kadar yorum gönderdin. Lütfen yarın tekrar dene.
Mesajınız bize başarılı bir şekilde ulaştırıldı. Teşekkürler.

Yorumlar

0 Yorum yok

Henüz yorum yapılmamış. İlk yorum yapan sen ol.

Blog Yazarı

Ömer Faruk Coşkun
Yazar
@ofcskn

İstanbul Üniversitesi Bilgisayar Mühendisliği Öğrencisi