Türler

Solidity statik olarak yazılmış bir dildir, bu, her bir değişkenin türünün (durum ve yerel) belirtilmesi gerektiği anlamına gelir. Solidity, karmaşık türler oluşturmak için bir araya getirilen birkaç temel tür sağlar.

Ayrıca, operatör içeren ifadelerde türler birbirleriyle etkileşime girebilirler. Çeşitli operatörlere göz atmak için, Operatörlerin Öncelik Sırası.

Solidity’de “tanımsız” veya “boş” değerler kavramı yoktur, yeni bildirilen değişkenlerin türüne bağlı olarak her zaman varsayılan bir değeri vardır. Beklenmeyen değerler ile uğraşırken, tüm işlemi geri almak için bir geri alma fonksiyonu kullanmalı ya da sonucu işaret eden ikinci bir bool değerine sahip bir veri demeti döndürmelisiniz.

Değer Türleri

Aşağıdaki türlere de değer türleri denir, çünkü bu türlerin değişkenleri her zaman değere göre iletilir, yani fonksiyon argümanları olarak veya atamalarda kullanıldıklarında her zaman kopyalanırlar.

Booleans

bool: Olası değerler true ve false sabitleridir.

Operatörler:

  • ! (Mantıksal olumsuzlama)

  • && (Mantıksal bağlaç, “ve”)

  • || (Mantıksal ayrılma, “veya”)

  • == (Eşitlik)

  • != (Eşitsizlik)

|| ve && operatörleri ortak kısa devre kurallarını uygular. Bunun anlamı f(x) || g(y), eğer f(x) true (doğru) olarak değerlendirilirse, g(y) yan etkileri olsa bile değerlendirilmeyecektir.

Tamsayılar

int / uint: Çeşitli boyutlarda işaretli ve işaretsiz tam sayılar. 8 (8’den 256 bit’e kadar işaretsiz) ve uint8 ila uint256 adımlarında uint8 ile uint256 arasındaki anahtar kelimeler. uint ve int sırasıyla uint256 ve int256 için takma adlardır.

Operatörler:

  • Karşılaştırmalar: <=, <, ==, !=, >=, > (bool olarak değerlendir)

  • Bit operatörleri: &, |, ^ (bit düzeyinde özel veya), ~ (bitsel olumsuzlama)

  • Değiştirme (Shift) operatörleri: << (sol shift), >> (sağ shift)

  • Aritmetik operatörler: +, -, tekli - (sadece imzalı tamsayılar için), *, /, % (mod alma operatörü), ** (ül alma operatörü)

Bir tamsayı türü olan X için, tür tarafından gösterilebilen minimum ve maksimum değere erişmek için type(X).min ve type(X).max ı kullanabilirsiniz.

Uyarı

Solidity’deki tamsayılar belirli bir aralıkla sınırlıdır. Örneğin, uint32 ile bu 0``dan ``2**32 - 1``e kadardır. Bu türlerde aritmetiğin gerçekleştirildiği iki mod vardır: "wrapping" veya "unchecked" mod ve "checked" mod. Varsayılan olarak, aritmetik her zaman "checked" durumundadır, yani bir işlemin sonucu türün değer aralığının dışına çıkarsa, çağrı bir :ref:`başarısız onaylama<asset-and-require>` aracılığıyla geri döndürülür. ``unchecked { ... } kullanarak “unchecked” moda geçebilirsiniz. Daha fazla ayrıntı unchecked ile ilgili bölümde bulunabilir.

Karşılaştırmalar

Bir karşılaştırmanın değeri, tamsayı değeri karşılaştırılarak elde edilen değerdir.

Bit işlemleri

Bit işlemleri, sayının ikisinin tümleyen gösterimi üzerinde gerçekleştirilir. Bu, örneğin ~int256(0) == int256(-1) anlamına gelir.

Shifts

Bir kaydırma işleminin sonucu, sol işlenenin türüne sahiptir ve sonucu türle eşleşecek şekilde kısaltır. Doğru işlenen imzasız türde olmalıdır, imzalı bir türle kaydırmaya çalışmak derleme hatası üretecektir.

Vardiyalar, aşağıdaki şekilde ikinin kuvvetleriyle çarpma kullanılarak “simüle edilebilir”. Sol işlenenin türünün kesilmesinin her zaman sonunda gerçekleştirildiğini, ancak açıkça belirtilmediğini unutmayın.

  • x << y, x * 2**y matematiksel ifadesine eşdeğerdir.

  • x >> y, x / 2**y matematiksel ifadesine eşdeğerdir, negatif sonsuza yuvarlanır.

Uyarı

0.5.0 sürümünden önce, negatif x için bir sağa kaydırma x >> y sıfıra yuvarlanmış x / 2**y matematiksel ifadesine eşdeğerdi, yani sağa kaydırmalar, aşağı yuvarlama (negatif sonsuza doğru) yerine yukarı (sıfıra doğru) yuvarlama olarak kullanılır.

Not

Aritmetik işlemlerde olduğu gibi kaydırma işlemleri için de taşma kontrolleri yapılmaz. Bunun yerine, sonuç her zaman kesilir.

Toplama, Çıkarma ve Çarpma

Toplama, çıkarma ve çarpma, taşma ve alttan akışa ilişkin iki farklı mod ile olağan semantiklere sahiptir:

Varsayılan olarak, tüm aritmetik yetersiz veya taşma açısından kontrol edilir, ancak bu, unchecked blok kullanılarak devre dışı bırakılabilir, bu da sarma aritmetiğiyle sonuçlanır. Daha fazla ayrıntı o bölümde bulunabilir.

-x ifadesi, (T(0) - x) ile eşdeğerdir; burada T, x``in türüdür. Yalnızca imzalı türlere uygulanabilir. ``x negatifse -x in değeri pozitif olabilir. İkisinin tamamlayıcı temsilinden kaynaklanan başka bir uyarı daha var:

int x = type(int).min; varsa, -x pozitif aralığa uymaz. unchecked { assert(-x == x); } çalışır ve işaretli modda kullanıldığında -x ifadesi başarısız bir onaylamaya neden olur.

Bölme

Bir işlemin sonucunun türü her zaman işlenenlerden birinin türü olduğundan, tamsayılarda bölme her zaman bir tamsayı ile sonuçlanır. Solidity’de bölme sıfıra doğru yuvarlanır. Bu, int256(-5) / int256(2) == int256(-2) anlamına gelir.

Buna karşılık, değişmezler (literals) üzerinde bölmenin keyfi kesinliğin kesirli değerleriyle sonuçlandığını unutmayın.

Not

Sıfıra bölme bir panik hatasına neden olur. Bu kontrol, unckecked { ... } ile devre dışı bırakılamaz.

Not

type(int).min / (-1) ifadesi, bölmenin taşmaya neden olduğu tek durumdur. Kontrollü aritmetik modda, bu başarısız bir onaylamaya neden olurken, sarma modunda değer type(int).min olacaktır.

Mod Alma

Mod alma işlemi a % n, a işleneninin n işlenenine bölünmesinden sonra kalan r``yi verir, burada ``q = int(a / n) ve r = a - (n * q). Bu, mod alma işleminin sol işleneni (veya sıfır) ile aynı işaretle sonuçlandığı ve a % n == -(-a % n)``nin negatif ``a için geçerli olduğu anlamına gelir:

  • int256(5) % int256(2) == int256(1)

  • int256(5) % int256(-2) == int256(1)

  • int256(-5) % int256(2) == int256(-1)

  • int256(-5) % int256(-2) == int256(-1)

Not

Sıfırlı mod alma işlemi Panik hatasına neden oluyor. Bu kontrol, unckecked { ... } ile devre dışı bırakılamaz.

Üs Alma

Üs, yalnızca üsteki işaretsiz türler için kullanılabilir. Elde edilen bir üs türü her zaman tabanın türüne eşittir. Lütfen sonucu tutacak ve olası onaylama hatalarına veya sarma davranışına hazırlanacak kadar büyük olmasına dikkat edin.

Not

İşaretli (checked) modda, üs alma yalnızca küçük tabanlar için nispeten ucuz exp işlem kodunu kullanır.

x**3 durumları için x*x*x ifadesi daha ucuz olabilir. Her durumda, gaz maliyeti testleri ve optimize edicinin kullanılması tavsiye edilir.

Not

0**0``ın EVM tarafından ``1 olarak tanımlandığını unutmayın.

Sabit Nokta Sayıları

Uyarı

Sabit nokta sayıları henüz Solidity tarafından tam olarak desteklenmemektedir. Bildirilebilirler, ancak atanamazlar veya atanamazlar.

fixed / ufixed: Çeşitli boyutlarda imzalı ve imzasız sabit nokta sayısı. Anahtar sözcükler ufixedMxN ve fixedMxN, burada M türün aldığı bit sayısını ve N kaç ondalık noktanın mevcut olduğunu gösterir. M 8’e bölünebilir olmalı ve 8’den 256 bit’e kadar gider. N 0 ile 80 arasında olmalıdır. ufixed ve fixed sırasıyla ufixed128x18 ve fixed128x18 için takma adlardır.

Operatörler:

  • Karşılaştırma: <=, <, ==, !=, >=, > (bool olarak değerlendir)

  • Aritmetik operatörler: +, -, tekil -, *, /, % (mod alma)

Not

Kayan nokta (birçok dilde float ve double, daha doğrusu IEEE 754 sayıları) ile sabit nokta sayıları arasındaki temel fark, tamsayı ve kesirli kısım için kullanılan bit sayısının (birçok dilde ondalık nokta) birincisinde esnektir, ikincisinde ise kesin olarak tanımlanmıştır. Genel olarak, kayan noktada neredeyse tüm alan sayıyı temsil etmek için kullanılırken, ondalık noktanın nerede olduğunu yalnızca az sayıda bit tanımlar.

Adresler

Adres türü, büyük ölçüde aynı olan iki şekilde gelir:

  • address: 20 baytlık bir değer tutar (bir Ethereum adresinin boyutu).

  • address payable: address ile aynıdır, ek olarak transfer ve send bulundurur.

Bu ayrımın arkasındaki fikir, address payable in, Ether gönderebileceğiniz bir adres olduğu, ancak Ether’i düz bir address e göndermemeniz gerektiğidir, örneğin akıllı bir sözleşme olabileceği için. Ether’i kabul etmek için oluşturulmamıştır.

Tür dönüşümleri:

address payable``den ``address``e örtülü dönüşümlere izin verilirken, ``address``den ``address payable``a dönüşümler ``payable(<address>) üzerinden açık olmalıdır.

uint160, tamsayı değişmezleri, bytes20 ve sözleşme türleri için address e ve adresten açık dönüşümlere izin verilir.

Yalnızca address ve sözleşme türündeki ifadeler, açık dönüştürme payable(...) aracılığıyla address payable türüne dönüştürülebilir. Sözleşme türü için, bu dönüştürmeye yalnızca sözleşme Ether alabiliyorsa, yani sözleşmenin bir alma veya ödenebilir yedek fonksiyonu varsa izin verilir. payable(0) ın geçerli olduğunu ve bu kuralın bir istisnası olduğunu unutmayın.

Not

address türünde bir değişkene ihtiyacınız varsa ve buna Ether göndermeyi planlıyorsanız, bu gereksinimi görünür kılmak için türünü address payable olarak bildirin. Ayrıca, bu ayrımı veya dönüşümü mümkün olduğunca erken yapmaya çalışın.

Operatörler:

  • <=, <, ==, !=, >= ve >

Uyarı

Daha büyük bir bayt boyutu kullanan bir türü bir address``e, örneğin ``bytes32``ye dönüştürürseniz, ``address kısaltılır. Dönüştürme belirsizliğini azaltmak için sürüm 0.4.24 ve derleyici kuvvetinin daha yüksek sürümü, dönüştürmede kesmeyi açık hale getirirsiniz.

Örneğin, 0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCC 32 bayt değerini alın.

address(uint160(bytes20(b))) kullanabilirsiniz, bu da 0x111122223333444455556666777788889999aAaa ile sonuçlanır,

veya 0x777788889999AaAAbBbbCccccddDdeeeEfFFfCcCc ile sonuçlanan address(uint160(uint256(b))) i kullanabilirsiniz.

Not

address ve address payable arasındaki ayrım, 0.5.0 sürümüyle tanıtıldı. Ayrıca bu versiyondan başlayarak, sözleşmeler adres türünden türetilmez, ancak yine de bir alma veya ödeme geri dönüş fonksiyonu varsa, açıkça address e veya address payable a dönüştürülebilir.

Adres Üyeleri

Adreslerin tüm üyelerine hızlıca göz atmak için, bkz.:ref:address_related.

  • balance and transfer

Bir adresin bakiyesini balance özelliğini kullanarak sorgulamak ve transfer fonksiyonunu kullanarak Ether’i (wei birimi cinsinden) bir ödenecek adrese göndermek mümkündür:

address payable x = payable(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

Mevcut sözleşmenin bakiyesi yeterince büyük değilse veya Ether transferi alıcı hesap tarafından reddedilirse transfer fonksiyonu başarısız olur. transfer fonksiyonu başarısızlık üzerine geri döner.

Not

x bir sözleşme (kontrat) adresiyse, kodu (daha spesifik olarak: varsa Receive Ether Fonksiyonu veya varsa Fallback Fonksiyonu yürütülür. transfer çağrısı ile birlikte (bu, EVM’nin bir özelliğidir ve engellenemez). Bu yürütmenin gazı biterse veya herhangi bir şekilde başarısız olursa, Ether transferi geri alınacak ve mevcut sözleşme bir istisna dışında durdurulacaktır.

  • send

Gönder, transfer``in alt düzey karşılığıdır. Yürütme (execution) başarısız olursa, mevcut sözleşme bir istisna dışında durmaz, ancak ``send, false döndürür.

Send is the low-level counterpart of transfer. If the execution fails, the current contract will not stop with an exception, but send will return false.

Uyarı

send kullanmanın bazı tehlikeleri vardır:

Çağrı yığını derinliği 1024 ise aktarım başarısız olur (bu her zaman arayan tarafından zorlanabilir) ve ayrıca alıcının gazı biterse de başarısız olur. Bu nedenle, güvenli Ether transferleri yapmak için her zaman send in dönüş değerini kontrol edin, transfer i kullanın veya daha iyisi: alıcının parayı çektiği bir kalıp kullanın.

  • call, delegatecall ve staticcall

ABI’ye uymayan sözleşmelerle arayüz oluşturmak veya kodlama üzerinde daha doğrudan kontrol sağlamak için call, delegatecall ve staticcall fonksiyonları sağlanmıştır. Hepsi tek bir bytes memory parametresi alır ve başarı koşulunu (bool olarak) ve döndürülen verileri (bytes memory) döndürür. Yapılandırılmış verileri kodlamak için abi.encode, abi.encodePacked, abi.encodeWithSelector ve abi.encodeWithSignature fonksiyonları kullanılabilir.

Örnek:

bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);

Uyarı

Tüm bu fonksiyonlar alt düzey fonksiyonlarıdır ve dikkatli kullanılmalıdır. Spesifik olarak, bilinmeyen herhangi bir sözleşme kötü niyetli olabilir ve onu çağırırsanız, kontrolü o sözleşmeye devredersiniz ve bu da sözleşmenize geri çağrı yapabilir, bu nedenle arama geri döndüğünde durum değişkenlerinizdeki değişikliklere hazır olun. Diğer sözleşmelerle etkileşime girmenin normal yolu, bir sözleşme nesnesi (x.f()) üzerindeki bir fonksiyonu çağırmaktır.

Not

Solidity’nin önceki sürümleri, bu fonksiyonların rastgele argümanlar almasına izin veriyordu ve ayrıca bytes4 türündeki ilk argümanı farklı şekilde ele alıyorlardı. Bu uç durumlar 0.5.0 sürümünde kaldırılmıştır.

Verilen gazı gas değiştiricisi ile ayarlamak mümkündür:

address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));

Benzer şekilde, sağlanan Ether değeri de kontrol edilebilir:

address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));

Son olarak, bu değiştiriciler birleştirilebilir. Onların sırası önemli değil:

address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));

Benzer şekilde, delegatecall fonksiyonu kullanılabilir: fark, yalnızca verilen adresin kodunun kullanılması, diğer tüm yönlerin (depolama, bakiye, …) mevcut sözleşmeden alınmasıdır. delegatecall un amacı, başka bir sözleşmede saklanan kütüphane kodunu kullanmaktır. Kullanıcı, her iki sözleşmedeki depolama düzeninin, kullanılacak temsilci çağrısı için uygun olduğundan emin olmalıdır.

Not

Homestead’den önce, orijinal msg.sender ve msg.value değerlerine erişim sağlamayan callcode adlı yalnızca sınırlı bir değişken mevcuttu. Bu fonksiyon 0.5.0 sürümünde kaldırılmıştır.

Bizans’tan (Byzantium) beri staticcall da kullanılabilir. Bu temelde call ile aynıdır, ancak çağrılan fonksiyon durumu herhangi bir şekilde değiştirirse geri döner.

Her üç fonksiyon, call, delegatecall ve staticcall çok düşük düzeyli fonksiyonlardır ve Solidity’nin tür güvenliğini bozdukları için yalnızca son çare olarak kullanılmalıdır.

Gas seçeneği her üç yöntemde de mevcuttur, value seçeneği ise yalnızca call da mevcuttur.

Not

Durumun okunması veya yazılmasından bağımsız olarak akıllı sözleşme kodunuzdaki sabit kodlanmış gaz değerlerine güvenmekten kaçınmak en iyisidir, çünkü bunun birçok tuzağı olabilir. Ayrıca, gelecekte gaza erişim değişebilir.

  • code and codehash

Herhangi bir akıllı sözleşme için dağıtılan kodu sorgulayabilirsiniz. EVM bayt kodunu boş olabilecek bir bytes memory olarak almak için .code kullanın. .codehash kullanın, bu kodun Keccak-256 karmasını alın (bytes32 olarak). addr.codehash``in ``keccak256(addr.code) kullanmaktan daha ucuz olduğunu unutmayın.

Not

Tüm sözleşmeler address türüne dönüştürülebilir, bu nedenle address(this).balance kullanılarak mevcut sözleşmenin bakiyesini sorgulamak mümkündür.

Sözleşme Türleri

Her sözleşme kendi türünü tanımlar. Sözleşmeleri dolaylı olarak miras aldıkları sözleşmelere dönüştürebilirsiniz. Sözleşmeler açıkça address türüne dönüştürülebilir.

address payable türüne ve address payable türünden açık dönüştürme, yalnızca sözleşme türünün bir alacak veya ödenebilir yedek fonksiyonu varsa mümkündür. Dönüştürme hala address(x) kullanılarak gerçekleştirilir. Sözleşme türünün bir alma veya ödenebilir yedek fonksiyonu yoksa, address payable``a dönüştürme ``payable(address(x)) kullanılarak yapılabilir.

Adres türü ile ilgili bölümde daha fazla bilgi bulabilirsiniz.

Not

0.5.0 sürümünden önce, sözleşmeler doğrudan adres türünden türetilir, ve address ve address payable arasında bir ayrım yoktu.

Sözleşme tipinde (MyContract c) yerel bir değişken bildirirseniz, o sözleşmedeki fonksiyonları çağırabilirsiniz. Aynı sözleşme türünden bir yerden atamaya özen gösterin.

Ayrıca sözleşmeleri somutlaştırabilirsiniz (bu, sözleşmelerin yeni oluşturuldukları anlamına gelir). Daha fazla ayrıntıyı ‘Contracts via new’ bölümünde bulabilirsiniz.

Bir sözleşmenin veri temsili, address türününkiyle aynıdır ve bu tür aynı zamanda ABI içinde kullanılır.

Sözleşmeler hiçbir operatörü desteklemez.

Sözleşme türlerinin üyeleri, public olarak işaretlenen tüm durum değişkenleri dahil olmak üzere sözleşmenin harici fonksiyonlarıdır.

Bir C sözleşmesi için, sözleşmeyle ilgili tür bilgisine erişmek için type(C) yi kullanabilirsiniz.

Sabit Boyutlu Bayt Dizileri

bytes1, bytes2, bytes3, …, bytes32 değer türleri 1’den 32’ye kadar bir bayt dizisini tutar.

Operatörler:

  • Karşılaştırmalar: <=, <, ==, !=, >=, > (bool olarak değerlendir)

  • Bit operatörleri: &, |, ^ (bit düzeyinde özel veya), ~ (bitsel olumsuzlama)

  • Shift operatörleri: << (sol shift), >> (sağ shift)

  • Dizin erişimi: x, bytesI türündeyse, 0 <= k < I için x[k], k ıncı baytı (salt okunur) döndürür.

Kaydırma operatörü, sağ işlenen olarak işaretsiz tamsayı türüyle çalışır (ancak sol işlenenin türünü döndürür), bu, kaydırılacak bit sayısını belirtir. İmzalı bir türe göre kaydırma, bir derleme hatası üretecektir.

Üyeler:

  • .length, bayt dizisinin sabit uzunluğunu verir (salt okunur).

Not

bytes1[] türü bir bayt dizisidir, ancak doldurma kuralları nedeniyle her öğe için (depolama dışında) 31 baytlık alan harcar. Bunun yerine bytes türünü kullanmak daha iyidir.

Not

0.8.0 sürümünden önce, byte, bytes1 için bir takma addı.

Dinamik Olarak Boyutlandırılmış Bayt Dizisi

bytes:

Dinamik olarak boyutlandırılmış bayt dizisi, bkz. Diziler. Bir değer türü değil!

string:

Dinamik olarak boyutlandırılmış UTF-8 kodlu dize, bkz.:ref:arrays. Bir değer türü değil!

Adres Değişmezleri

Adres sağlama toplamı (checksum) testini geçen onaltılık sabit değerler, örneğin 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF, address türündedir.

39 ila 41 basamak uzunluğunda olan ve sağlama toplamı (checksum) testini geçmeyen onaltılık değişmez değerler bir hata üretir. Hatayı kaldırmak için başa (tamsayı türleri için) veya sona(bytesNN türleri için) sıfırlar ekleyebilirsiniz.

Not

Karışık büyük/küçük harfli adres sağlama toplamı biçimi, EIP-55 içinde tanımlanır.

Rasyonel ve Tamsayı Değişmezleri

Tamsayı değişmezleri, 0-9 aralığında bir basamak dizisinden oluşturulur. Ondalık sayılar olarak yorumlanırlar. Örneğin, 69 altmış dokuz anlamına gelir. Solidity’de sekizlik değişmez değerler yoktur ve baştaki sıfırlar geçersizdir.

Ondalık kesirli değişmezler, .’nın ardından en az bir sayı yerleştirilmesi ile oluşturulur. Örnekler arasında .1 ve 1.3 bulunur (1. geçersizdir).

Mantisin kesirli olabileceği ancak üssün bir tamsayı olması gereken 2e10 şeklindeki bilimsel gösterim de desteklenmektedir. MeE değişmez değeri, M * 10**E ile eşdeğerdir. Örnekler arasında 2e10, -2e10, 2e-10, 2.5e1 yer alır.

Okunabilirliğe yardımcı olmak için sayısal bir hazır bilginin basamaklarını ayırmak için alt çizgiler kullanılabilir. Örneğin, ondalık (decimal) 123_000, onaltılık (hexadecimal) 0x2eff_abde, bilimsel ondalık gösterim 1_2e345_678 hepsi geçerlidir. Alt çizgiye yalnızca iki basamak arasında izin verilir ve yalnızca bir ardışık alt çizgiye izin verilir. Alt çizgi içeren bir sayı değişmezine ek bir anlamsal anlam eklenmez, alt çizgiler yoksayılır.

Sayı değişmezi ifadeleri, sabit olmayan bir türe dönüştürülene kadar (yani, bunları bir sayı değişmezi ifadesi (boolean değişmezleri gibi) dışında herhangi bir şeyle birlikte kullanarak veya açık dönüştürme yoluyla) isteğe bağlı kesinliği korur. Bu, hesaplamaların taşmadığı ve bölmelerin sayı değişmez ifadelerinde kesilmediği anlamına gelir.

Örneğin, (2**800 + 1) - 2**800, ara sonuçlar makine kelime boyutuna bile sığmasa da 1 sabitiyle sonuçlanır (uint8 türünden). Ayrıca, .5 * 8, 4 tamsayısıyla sonuçlanır (arada tamsayı olmayanlar kullanılmasına rağmen).

Uyarı

Çoğu operatör, değişmez değerlere uygulandığında değişmez bir ifade üretirken, bu kalıbı takip etmeyen bazı operatörler vardır:

  • Üçlü operatör (... ? ... : ...),

  • Dizi alt simgesi (subscript) (<array>[<index>]).

255 + (true ? 1 : 0) veya 255 + [1, 2, 3][0] gibi ifadelerin doğrudan 256 değişmezini kullanmaya eşdeğer olmasını bekleyebilirsiniz, ancak aslında bunlar uint8 türünde hesaplanır ve taşabilir.

Tamsayılara uygulanabilen herhangi bir operatör, işlenenler tamsayı olduğu sürece sayı değişmez ifadelerine de uygulanabilir. İkisinden herhangi biri kesirliyse, bit işlemlerine izin verilmez ve üs kesirliyse üs almaya izin verilmez (çünkü bu rasyonel olmayan bir sayıya neden olabilir).

Sol (veya taban) işlenen olarak değişmez sayılar ve sağ (üs) işlenen olarak tamsayı türleri ile kaydırmalar ve üs alma, her zaman “uint256” (negatif olmayan değişmezler için) veya sağ (üs) işlenenin türünden bağımsız olarak “int256” (negatif değişmezler için) içinde gerçekleştirilir.

Uyarı

0.4.0 sürümünden önce Solidity’de tamsayı değişmezleri üzerinde bölme kullanılırdı, ancak şimdi rasyonel bir sayıya dönüştürülür, yani 5 / 2, 2 ye eşit değil, 2.5 e eşittir .

Not

Solidity, her rasyonel sayı için bir sayı değişmez (literal) tipine sahiptir. Tamsayı değişmezleri ve rasyonel sayı değişmezleri, sayı değişmez türlerine aittir. Ayrıca, tüm sayı değişmez ifadeleri (yani yalnızca sayı değişmezlerini ve işleçlerini içeren ifadeler) sayı değişmez türlerine aittir. Dolayısıyla, 1 + 2 ve 2 + 1 sayı değişmez ifadelerinin her ikisi de üç rasyonel sayı için aynı sayı değişmez türüne aittir.

Not

Sayı değişmez ifadeleri, değişmez olmayan ifadelerle birlikte kullanılır kullanılmaz, değişmez bir türe dönüştürülür. Türlerden bağımsız olarak, aşağıdaki b``ye atanan ifadenin değeri bir tamsayı olarak değerlendirilir. "a", "uint128" türünde olduğundan, "2.5 + a" ifadesinin uygun bir türe sahip olması gerekir. ``2.5 ve uint128 tipi için ortak bir tip olmadığı için Solidity derleyicisi bu kodu kabul etmez.

uint128 a = 1;
uint128 b = 2.5 + a + 0.5;

Dize Değişmezleri ve Türleri

Dize değişmezleri ya çift ya da tek tırnak ("foo" veya 'bar') ile yazılır ve ayrıca uzun dizelerle uğraşırken yardımcı olabilecek şekilde birden çok ardışık parçaya bölünebilirler ("foo" "bar", "foobar" ile eşdeğerdir). C’deki gibi sondaki sıfırları ima etmezler; "foo" dört değil, üç baytı temsil eder. Tamsayı değişmezlerinde olduğu gibi, türleri değişebilir, ancak sığarlarsa “bytes1”, …, “bytes32”ye örtük olarak “bytes” ve “string”e dönüştürülebilirler.

Örneğin, bytes32 samevar = "stringliteral" ile dize değişmezi, bir bytes32 türüne atandığında ham bayt biçiminde yorumlanır.

Dize değişmezleri yalnızca yazdırılabilir ASCII karakterleri içerebilir; bu, 0x20 .. 0x7E arasındaki ve dahil olan karakterler anlamına gelir.

Ayrıca, dize değişmezleri aşağıdaki kaçış karakterlerini de destekler:

  • \<newline> (gerçek bir yeni satırdan kaçar)

  • \\ (ters eğik çizgi)

  • \' (tek alıntı)

  • \" (çift alıntı)

  • \n (Yeni satır)

  • \r (satırbaşı)

  • \t (etiket)

  • \xNN (hex kaçış, aşağıya bakınız)

  • \uNNNN (unicode kaçış, aşağıya bakınız)

\xNN bir onaltılık değer alıp uygun baytı eklerken, \uNNNN bir Unicode kod noktası alır ve bir UTF-8 dizisi ekler.

Not

0.8.0 sürümüne kadar üç ek kaçış dizisi vardı: \b, \f ve \v. Diğer dillerde yaygın olarak bulunurlar, ancak pratikte nadiren ihtiyaç duyulur. Bunlara ihtiyacınız varsa, yine de diğer ASCII karakterleri gibi, sırasıyla \x08, \x0c ve \x0b gibi onaltılık çıkışlar yoluyla eklenebilirler.

Aşağıdaki örnekteki dizenin uzunluğu on bayttır. Yeni satır baytı ile başlar, ardından çift tırnak, tek tırnak, ters eğik çizgi ve ardından (ayırıcı olmadan) abcdef karakter dizisi gelir.

"\n\"\'\\abc\
def"

Yeni satır olmayan herhangi bir Unicode satır sonlandırıcı (yani LF, VF, FF, CR, NEL, LS, PS) dize değişmezini sonlandırdığı kabul edilir. Yeni satır, yalnızca önünde bir \ yoksa dize değişmezini sonlandırır.

Unicode Değişmezler

Normal dize değişmezleri yalnızca ASCII içerebilirken, Unicode değişmezleri unicode – anahtar kelimesiyle önek – herhangi bir geçerli UTF-8 dizisi içerebilir. Ayrıca, normal dize değişmezleri ile aynı kaçış dizilerini de desteklerler.

string memory a = unicode"Hello 😃";

Onaltılık (Hexadecimal) Değişmezler

Onaltılık değişmezlerin önüne hex anahtar kelimesi getirilir ve çift veya tek tırnak içine alınır (hex"001122FF" , hex'0011_22_FF' ). İçerikleri, isteğe bağlı olarak bayt sınırları arasında ayırıcı olarak tek bir alt çizgi kullanabilen onaltılık basamaklar olmalıdır. Değişmez değerin değeri, onaltılık dizinin ikili gösterimi olacaktır.

Boşlukla ayrılmış birden çok onaltılık sabit değer, tek bir sabit değerde birleştirilir: hex"00112233" hex"44556677" , hex"0011223344556677" ye eşittir

Onaltılık değişmez değerler string değişmezleri gibi davranır ve aynı dönüştürülebilirlik kısıtlamalarına sahiptir.

Numaralandırmalar (Enums)

Numaralandırmalar, Solidity’de kullanıcı tanımlı bir tür oluşturmanın bir yoludur. Tüm tamsayı türlerine açıkça dönüştürülebilirler, ancak örtük dönüştürmeye izin verilmez. Tamsayıdan yapılan açık dönüştürme, çalışma zamanında değerin numaralandırma aralığı içinde olup olmadığını kontrol eder ve aksi takdirde bir Panik hatası oluşmasına neden olur. Numaralandırmalar en az bir üye gerektirir ve bildirildiğinde varsayılan değeri ilk üyedir. Numaralandırmaların 256’dan fazla üyesi olamaz.

Veri gösterimi, C’deki numaralandırmalarla aynıdır: Seçenekler, 0 dan başlayan müteakip işaretsiz tamsayı değerleriyle temsil edilir.

type(NameOfEnum).min ve type(NameOfEnum).max kullanarak verilen numaralandırmanın en küçük ve sırasıyla en büyük değerini alabilirsiniz.

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

contract test {
    enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choice;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;

    function setGoStraight() public {
        choice = ActionChoices.GoStraight;
    }

    // Enum türleri ABI'nin bir parçası olmadığından, Solidity'nin dışındaki tüm konular için "getChoice" imzası otomatik olarak "getChoice() returns (uint8)" olarak değiştirilecektir.
    function getChoice() public view returns (ActionChoices) {
        return choice;
    }

    function getDefaultChoice() public pure returns (uint) {
        return uint(defaultChoice);
    }

    function getLargestValue() public pure returns (ActionChoices) {
        return type(ActionChoices).max;
    }

    function getSmallestValue() public pure returns (ActionChoices) {
        return type(ActionChoices).min;
    }
}

Not

Numaralandırmalar, sözleşme veya kitaplık tanımlarının dışında dosya düzeyinde de bildirilebilir.

Kullanıcı Tanımlı Değer Türleri

Kullanıcı tanımlı bir değer türü, bir temel değer türü üzerinde sıfır maliyetli bir soyutlama oluşturmaya izin verir. Bu, takma ada benzer, ancak daha katı tür gereksinimleri vardır.

Kullanıcı tanımlı bir değer türü, type C is V kullanılarak tanımlanır; burada C yeni tanıtılan türün adıdır ve V yerleşik bir değer türü olmalıdır (“altta yatan tip”/ “underlying type”). C.wrap fonksiyonu, temeldeki türden özel türe dönüştürmek için kullanılır. Benzer şekilde, özel türden temel türe dönüştürmek için C.unwrap fonksiyonu kullanılır.

C türünün herhangi bir işleci veya bağlı üye fonksiyonu yoktur. Özellikle, == operatörü bile tanımlanmamıştır. Diğer türlere ve diğer türlerden açık ve örtük dönüştürmelere izin verilmez.

Bu türlerin değerlerinin veri temsili, temeldeki türden devralınır ve temel alınan tür de ABI’da kullanılır.

Aşağıdaki örnek, 18 ondalık basamaklı bir ondalık sabit nokta türünü ve tür üzerinde aritmetik işlemler yapmak için bir minimum kitaplığı temsil eden özel bir UFixed256x18 türünü gösterir.

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

// Kullanıcı tanımlı bir değer türü kullanarak 18 ondalık, 256 bit genişliğinde sabit nokta türünü temsil eder.
type UFixed256x18 is uint256;

/// UFixed256x18 üzerinde sabit nokta işlemleri yapmak için minimal bir kütüphane.
library FixedMath {
    uint constant multiplier = 10**18;

    ///İki UFixed256x18 sayısı ekler. uint256'da kontrol edilen aritmetiği temel alarak taşma durumunda geri döner.
    function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
    }
    /// UFixed256x18 ve uint256'yı çarpar. uint256'da kontrol edilen aritmetiği temel alarak taşma durumunda geri döner.
    function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
    }
    /// UFixed256x18 numarasının zeminini alın.
    /// "a"yı geçmeyen en büyük tamsayıyı döndürür.
    function floor(UFixed256x18 a) internal pure returns (uint256) {
        return UFixed256x18.unwrap(a) / multiplier;
    }
    /// Bir uint256'yı aynı değerde bir UFixed256x18'e dönüştürür.
    /// Tamsayı çok büyükse geri döner.
    function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) {
        return UFixed256x18.wrap(a * multiplier);
    }
}

UFixed256x18.wrap ve FixedMath.toUFixed256x18 öğelerinin nasıl aynı imzaya sahip olduğuna, ancak çok farklı iki işlem gerçekleştirdiğine dikkat edin: UFixed256x18.wrap işlevi, girişle aynı veri temsiline sahip bir UFixed256x18 döndürürken, toUFixed256x18, aynı sayısal değere sahip bir UFixed256x18 döndürür.

Fonksiyon Tipleri

Fonksiyon türleri, kullanulan fonksiyonların türleridir. Fonksiyon tipinin değişkenleri fonksiyonlardan atanabilir ve fonksiyon tipinin fonksiyon parametreleri fonksiyon çağrılarına fonksiyon geçirmek ve fonksiyon çağrılarından fonksiyon döndürmek için kullanılabilir. Fonksiyon türleri iki şekilde gelir - dahili ve harici fonksiyonlar:

Dahili fonksiyonlar, yalnızca geçerli sözleşmenin içinde (daha spesifik olarak, dahili kitaplık fonksiyonları ve devralınan fonksiyonları da içeren geçerli kod biriminin içinde) çağrılabilir çünkü bunlar geçerli sözleşmenin bağlamı dışında yürütülemezler. Dahili bir fonkaiyonu çağırmak, tıpkı mevcut sözleşmenin bir fonksiyonunu dahili olarak çağırırken olduğu gibi, giriş etiketine atlanarak gerçekleştirilir.

Harici fonksiyonlar bir adres ve bir işlev imzasından oluşur ve bunlar iletilebilir ve harici fonksiyon çağrılarından döndürülebilir.

Fonksiyon türleri aşağıdaki gibi not edilir:

function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]

Parametre türlerinin aksine, dönüş türleri boş olamaz - fonksiyonun türünün hiçbir şey döndürmemesi gerekiyorsa, returns (<return types>) bölümünün tamamı atlanmalıdır.

Varsayılan olarak, fonksiyon türleri dahilidir, bu nedenle internal anahtar sözcüğü atlanabilir. Bunun yalnızca fonksiyon türleri için geçerli olduğunu unutmayın. Sözleşmelerde tanımlanan fonksiyonlar için görünürlük açıkça belirtilmelidir, varsayılan değer yoktur.

Dönüşümler:

A fonksiyon türü, yalnızca ve yalnızca parametre türleri aynıysa, dönüş türleri aynıysa, dahili/harici özellikleri aynıysa ve A öğesinin durum değişkenliği aynıysa, dolaylı olarak B işlev türüne dönüştürülebilir. A, B durum değişkenliğinden daha kısıtlayıcıdır. Özellikle:

  • pure fonksiyonlar, view ve non-payable fonksiyonlara dönüştürülebilir

  • view fonksiyonları non-payable fonksiyonlara dönüştürülebilir

  • payable fonksiyonlar non-payable fonksiyonlara dönüştürülebilir

Fonksiyon türleri arasında başka hiçbir dönüşüm mümkün değildir.

payable ve non-payable fonksiyonlarla alakalı kural biraz kafa karıştırıcı olabilir, ancak özünde, bir fonksiyon payable ise, bu aynı zamanda sıfır Ether ödemesini de kabul ettiği anlamına gelir, yani bu fonksiyon atrıca non-payable``dır. Öte yandan, bir ``non-payable fonksiyon kendisine gönderilen Ether’i reddedecektir, bu nedenle non-payable fonksiyonlar payable fonksiyonlara dönüştürülemez.

Bir fonksiyon türü değişkeni başlatılmazsa, onu çağırmak bir Panik hatası ile sonuçlanır. Aynısı, bir fonksiyon üzerinde delete kullandıktan sonra çağırırsanız da olur.

Harici fonksiyon türleri, Solidity bağlamı dışında kullanılırsa, adres ve ardından fonksiyon tanımlayıcısını birlikte tek bir bytes24 türünde kodlayan function türü olarak kabul edilirler.

Mevcut sözleşmenin genel (public) fonksiyonlarının hem dahili hem de harici (external) bir fonksiyon olarak kullanılabileceğini unutmayın. f yi dahili bir fonksiyon olarak kullanmak için f yi kullanın, harici biçimini kullanmak istiyorsanız this.f yi kullanın.

Dahili tipte bir fonksiyon, nerede tanımlandığına bakılmaksızın dahili fonksiyon tipindeki bir değişkene atanabilir. Bu, hem sözleşmelerin hem de kütüphanelerin özel, dahili ve genel fonksiyonlarını ve ayrıca ücretsiz fonksiyonlarını içerir. harici fonksiyon türleri ise yalnızca genel (public) ve harici (external) sözleşme fonksiyonlarıyla uyumludur. Kitaplıklar, bir delegatecall gerektirdikleri ve seçicileri için farklı bir ABI kuralı kullandıkları için hariç tutulur. Arayüzlerde bildirilen fonksiyonların tanımları yoktur, bu nedenle onlara işaret etmek de bir anlam ifade etmez.

Üyeler:

Harici (veya genel) fonksiyonlar aşağıdaki üyelere sahiptir:

  • .address fonksiyonun sözleşmesinin adresini döndürür.

  • .selector, BI işlev seçicisini döndürür

Not

Harici (veya genel) fonksiyonlar, .gas(uint) ve .value(uint) ek üyelerine sahiptiler. Bunlar Solidity 0.6.2’de tartışmaya açıldı ve Solidity 0.7.0’da kaldırıldı. Bunun yerine, bir fonksiyona gönderilen gaz miktarını veya wei miktarını belirtmek için sırasıyla {gas: ...} ve {value: ...} kullanın. Daha fazla bilgi için bkz. External Function Calls .

Üyelerin nasıl kullanılacağını gösteren örnek:

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

contract Example {
    function f() public payable returns (bytes4) {
        assert(this.f.address == address(this));
        return this.f.selector;
    }

    function g() public {
        this.f{gas: 10, value: 800}();
    }
}

Dahili fonksiyon türlerinin nasıl kullanılacağını gösteren örnek:

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

library ArrayUtils {
    // aynı kod bağlamının parçası olacakları için dahili fonksiyonlar dahili kütüphane fonksiyonlarında kullanılabilir
    function map(uint[] memory self, function (uint) pure returns (uint) f)
        internal
        pure
        returns (uint[] memory r)
    {
        r = new uint[](self.length);
        for (uint i = 0; i < self.length; i++) {
            r[i] = f(self[i]);
        }
    }

    function reduce(
        uint[] memory self,
        function (uint, uint) pure returns (uint) f
    )
        internal
        pure
        returns (uint r)
    {
        r = self[0];
        for (uint i = 1; i < self.length; i++) {
            r = f(r, self[i]);
        }
    }

    function range(uint length) internal pure returns (uint[] memory r) {
        r = new uint[](length);
        for (uint i = 0; i < r.length; i++) {
            r[i] = i;
        }
    }
}


contract Pyramid {
    using ArrayUtils for *;

    function pyramid(uint l) public pure returns (uint) {
        return ArrayUtils.range(l).map(square).reduce(sum);
    }

    function square(uint x) internal pure returns (uint) {
        return x * x;
    }

    function sum(uint x, uint y) internal pure returns (uint) {
        return x + y;
    }
}

Harici işlev türlerini kullanan başka bir örnek:

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


contract Oracle {
    struct Request {
        bytes data;
        function(uint) external callback;
    }

    Request[] private requests;
    event NewRequest(uint);

    function query(bytes memory data, function(uint) external callback) public {
        requests.push(Request(data, callback));
        emit NewRequest(requests.length - 1);
    }

    function reply(uint requestID, uint response) public {
        // Cevabın güvenilir bir kaynaktan gelip gelmediği kontrol edilir
        requests[requestID].callback(response);
    }
}


contract OracleUser {
    Oracle constant private ORACLE_CONST = Oracle(address(0x00000000219ab540356cBB839Cbe05303d7705Fa)); // known contract
    uint private exchangeRate;

    function buySomething() public {
        ORACLE_CONST.query("USD", this.oracleResponse);
    }

    function oracleResponse(uint response) public {
        require(
            msg.sender == address(ORACLE_CONST),
            "Only oracle can call this."
        );
        exchangeRate = response;
    }
}

Not

Lambda veya satır içi işlevler planlanmıştır ancak henüz desteklenmemektedir.

Referans Türleri

Referans türünün değerleri, birden çok farklı adla değiştirilebilir. Bunu, bir değer türü değişkeni kullanıldığında bağımsız bir kopya aldığınız değer türleriyle karşılaştırın. Bu nedenle referans türleri, değer türlerinden daha dikkatli ele alınmalıdır. Şu anda referans türleri yapılar, diziler ve eşlemelerden oluşmaktadır. Bir referans türü kullanıyorsanız, her zaman türün depolandığı veri alanını açıkça sağlamanız gerekir: memory (ömrü, harici bir fonksiyon çağrısıyla sınırlıdır), storage (durum değişkenlerinin ömrünün, bir sözleşmenin ömrüyle sınırlı olduğu durumlarda saklanır) veya calldata (fonksiyon argümanlarını içeren özel veri konumu).

Veri konumunu değiştiren bir atama veya tür dönüştürme işlemi her zaman otomatik bir kopyalama işlemine neden olurken, aynı veri konumu içindeki atamalar yalnızca bazı durumlarda depolama türleri için kopyalanır.

Veri Konumu

Her referans türünün, nerede depolandığı hakkında “veri konumu” olan ek bir açıklaması vardır. Üç veri konumu vardır: memory, storage ve calldata. Çağrı verileri (calldata), fonksiyon bağımsız değişkenlerinin depolandığı ve çoğunlukla bellek gibi davrandığı, değiştirilemeyen, kalıcı olmayan bir alandır.

Not

Yapabiliyorsanız, veri konumu olarak calldata kullanmayı deneyin, çünkü bu kopyaları önler ve ayrıca verilerin değiştirilememesini sağlar. “calldata” veri konumuna sahip diziler ve yapılar da fonksiyonlarla döndürülebilir, ancak bu türlerin atanması mümkün değildir.

Not

0.6.9 sürümünden önce, referans türü argümanlar için veri konumu, harici fonksiyonlarda calldata , genel fonksiyonlarda memory ve dahili ve özel fonksiyonlarda memory veya storage ile sınırlıydı. . Artık memory e ve calldata ya, görünürlüklerinden bağımsız olarak tüm fonksiyonlarda izin verilir.

Not

0.5.0 sürümünden önce, veri konumu atlanabilir ve değişkenin türüne, fonksiyon türüne vb. bağlı olarak varsayılan olarak farklı konumlara atanırdı, ancak tüm karmaşık türler şimdi açık bir veri konumu vermelidir.

Veri Konumu ve Atama Davranışı

Veri konumları yalnızca verilerin kalıcılığı için değil, aynı zamanda atamaların anlamı için de önemlidir:

Data locations are not only relevant for persistency of data, but also for the semantics of assignments:

  • storage ve memory (veya calldata) arasındaki atamalar her zaman bağımsız bir kopya oluşturur.

  • memory``den ``memory``ye (bellekten belleğe) yapılan atamalar yalnızca referans oluşturur. Bu, bir bellek değişkeninde (``memory) yapılan değişikliklerin aynı verilere atıfta bulunan diğer tüm bellek değişkenlerinde de görülebileceği anlamına gelir.

  • storage dan (depolamadan), local (yerel) depolama değişkenine yapılan atamalar da yalnızca bir referans atar.

  • Diğer tüm atamalar storage a her zaman kopyalanır. Bu duruma örnek olarak, yerel değişkenin kendisi yalnızca bir başvuru olsa bile, durum değişkenlerine veya depolama yapısı türünün yerel değişkenlerinin üyelerine atamalar verilebilir.

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

contract C {
    // x'in veri konumu depolamadır.
    // Bu, veri konumunun atlanabileceği tek yerdir.
    uint[] x;

    // memoryArray öğesinin veri konumu bellektir.
    function f(uint[] memory memoryArray) public {
        x = memoryArray; // çalışır ve tüm diziyi depoya kopyalar
        uint[] storage y = x; // çalışır ve bir işaretçi atar. y'nin veri konumu depolamadır
        y[7]; // 8. öğeyi döndürür
        y.pop(); // x'i y ile değiştirir
        delete x; // diziyi temizler, ayrıca y'yi değiştirir
        // Aşağıdakiler çalışmıyor; depolamada yeni bir geçici adsız dizi oluşturması gerekir, ancak depolama "statik olarak" tahsis edilir: /
        // y = memoryArray;
        // İşaretçiyi "sıfırlayacağı" için bu da işe yaramaz, ancak işaret edebileceği mantıklı bir konum yoktur.
        // delete y;
        g(x); // g'yi çağırır, x'e bir referans verir
        h(x); // h'yi çağırır ve bellekte bağımsız, geçici bir kopya oluşturur
    }

    function g(uint[] storage) internal pure {}
    function h(uint[] memory) public pure {}
}

Diziler

Diziler, derleme zamanında sabit bir boyuta sahip olabilir veya dinamik bir boyuta sahip olabilir.

Sabit boyutlu bir dizinin türü k ve öğe türü T, T[k] olarak yazılır ve dinamik boyut dizisi T[] olarak yazılır.

Örneğin, uint in 5 dinamik dizisinden oluşan bir dizi uint[][5] olarak yazılır. Notasyon, diğer bazı dillere kıyasla tersine çevrilir. Solidity’de, X[3] her zaman X türünde üç öğe içeren bir dizidir, X in kendisi bir dizi olsa bile. C gibi diğer dillerde durum böyle değildir.

Endeksler sıfır tabanlıdır ve erişim bildirimin tersi yönündedir.

Örneğin, bir uint[][5] memory x değişkeniniz varsa, x[2][6] kullanarak üçüncü dinamik dizi içerisindeki yedinci uint’e erişirsiniz ve üçüncü dinamik diziye erişmek için x[2] kullanırsınız. Yine, aynı zamanda bir dizi de olabilen bir T türü için bir T[5] a diziniz varsa, o zaman a[2] her zaman T tipine sahiptir.

Dizi öğeleri, eşleme veya yapı dahil olmak üzere herhangi bir türde olabilir. Türler için genel kısıtlamalar geçerlidir, çünkü eşlemeler yalnızca “depolama” veri konumunda depolanabilir ve genel olarak görülebilen fonksiyonlar ABI types olan parametrelere ihtiyaç duyar.

Durum değişkeni dizilerini public olarak işaretlemek ve Solidity’nin bir alıcı oluşturmasını sağlamak mümkündür. Sayısal dizin, alıcı için gerekli bir parametre haline gelir.

Sonunu aşan bir diziye erişmek, başarısız bir onaylamaya neden olur. .push() ve .push(value) yöntemleri dizinin sonuna yeni bir öğe eklemek için kullanılabilir; burada .push() sıfır başlatılmış bir öğe ekler ve ona bir referans döndürür.

Diziler olarak bytes ve string

bytes ve string türündeki değişkenler özel dizilerdir. bytes türü bytes1[] ile benzerdir, ancak çağrı verileri ve bellekte sıkıca paketlenmiştir. string, bytes değerine eşittir ancak uzunluk veya dizin erişimine izin vermez.

Solidity’nin string işleme fonksiyonları yoktur, ancak üçüncü taraf string kitaplıkları vardır. Ayrıca, keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)) kullanarak iki dizgiyi keccak256-hash ile karşılaştırabilir ve string.concat(s1, s2) kullanarak iki dizgiyi birleştirebilirsiniz.

bytes1[] yerine bytes kullanmalısınız çünkü daha ucuzdur, çünkü memory``de ``bytes1[] kullanmak, öğeler arasında 31 dolgu bayt ekler. storage”da, sıkı paketleme nedeniyle dolgu bulunmadığına dikkat edin, bkz. bayt ve string. Genel bir kural olarak, rastgele uzunluktaki ham bayt verileri için bytes ve rastgele uzunluktaki string (UTF-8) verileri için string kullanın. Uzunluğu belirli bir bayt sayısıyla sınırlayabiliyorsanız, her zaman bytes1 ile bytes32 arasındaki değer türlerinden birini kullanın çünkü bunlar çok daha ucuzdur.

Not

s stringinin bayt temsiline erişmek istiyorsanız, bytes(s).length / bytes(s)[7] = 'x'; yapısını kullanın. Tek tek karakterlere değil, UTF-8 temsilinin düşük seviyeli baytlarına eriştiğinizi unutmayın.

bytes.concat ve string.concat Fonksiyonları

string.concat kullanarak rastgele sayıda string değerini birleştirebilirsiniz. Fonksiyon, bağımsız değişkenlerin içeriğini doldurmadan içeren tek bir string memory dizisi döndürür. Örtülü olarak string e dönüştürülemeyen diğer türlerin parametrelerini kullanmak istiyorsanız, önce bunları string e dönüştürmeniz gerekir.

Benzer şekilde, bytes.concat fonksiyonu, rastgele sayıda bytes veya bytes1 ... bytes32 değerlerini birleştirebilir. Fonksiyon, bağımsız değişkenlerin içeriğini doldurmadan içeren tek bir bytes memory dizisi döndürür. String parametreleri veya örtük olarak bytes a dönüştürülemeyen diğer türleri kullanmak istiyorsanız, önce bunları bytes veya bytes1 /…/ bytes32 ye dönüştürmeniz gerekir.

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

contract C {
    string s = "Storage";
    function f(bytes calldata bc, string memory sm, bytes16 b) public view {
        string memory concatString = string.concat(s, string(bc), "Literal", sm);
        assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concatString).length);

        bytes memory concatBytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
        assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concatBytes.length);
    }
}

string.concat ı veya bytes.concat ı, argüman olmadan çağırırsanız, boş bir dizi döndürürler.

Bellek Dizilerini Ayırma

Dinamik uzunluktaki bellek dizileri new operatörü kullanılarak oluşturulabilir. Depolama dizilerinin aksine, bellek dizilerini yeniden boyutlandırmak değildir (ör. .push üye fonksiyonları kullanılamaz). Gereken boyutu önceden hesaplamanız veya yeni bir bellek dizisi oluşturmanız ve her öğeyi kopyalamanız gerekir.

Solidity’deki tüm değişkenler gibi, yeni tahsis edilen dizilerin öğeleri her zaman varsayılan değer ile başlatılır.

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

contract C {
    function f(uint len) public pure {
        uint[] memory a = new uint[](7);
        bytes memory b = new bytes(len);
        assert(a.length == 7);
        assert(b.length == len);
        a[6] = 8;
    }
}

Dizi İfadeleri

Bir dizi ifadesi, köşeli parantezler ([...]) içine alınmış bir veya daha fazla ifadenin virgülle ayrılmış bir listesidir. Örneğin [1, a, f(3)]. Dizi ifadesinin türü şu şekilde belirlenir:

Her zaman uzunluğu ifade sayısı olan statik olarak boyutlandırılmış bir bellek dizisidir.

Dizinin temel türü, diğer tüm ifadelerin dolaylı olarak kendisine dönüştürülebileceği şekilde listedeki ilk ifadenin türüdür. Bu mümkün değilse bir tür hatasıdır.

Tüm öğelerin dönüştürülebileceği bir türün olması yeterli değildir. Öğelerden birinin bu türden olması gerekir.

Aşağıdaki örnekte, [1, 2, 3] türü uint8[3] memory dir, çünkü bu sabitlerin her birinin türü uint8 dir. Sonucun uint[3] memory türünde olmasını istiyorsanız, ilk öğeyi uint e dönüştürmeniz gerekir.

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

contract C {
    function f() public pure {
        g([uint(1), 2, 3]);
    }
    function g(uint[3] memory) public pure {
        // ...
    }
}

Birinci ifadenin türü uint8 iken ikincinin türü int8 olduğundan ve bunlar örtük olarak birbirine dönüştürülemediğinden [1, -1] dizisi ifadesi geçersizdir. Çalışması için örneğin [int8(1), -1] kullanabilirsiniz.

Farklı türdeki sabit boyutlu bellek dizileri birbirine dönüştürülemediğinden (temel türler yapabilse bile), iki boyutlu dizi ifadelerini kullanmak istiyorsanız, her zaman ortak bir temel türü açıkça belirtmeniz gerekir:

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

contract C {
    function f() public pure returns (uint24[2][4] memory) {
        uint24[2][4] memory x = [[uint24(0x1), 1], [0xffffff, 2], [uint24(0xff), 3], [uint24(0xffff), 4]];
        // Aşağıdakiler çalışmaz, çünkü bazı iç diziler doğru tipte değildir.
        // uint[2][4] memory x = [[0x1, 1], [0xffffff, 2], [0xff, 3], [0xffff, 4]];
        return x;
    }
}

Sabit boyutlu bellek dizileri, dinamik olarak boyutlandırılmış bellek dizilerine atanamaz, yani aşağıdakiler mümkün değildir:

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

// Bu derleme gerçekleşmeyecek.
contract C {
    function f() public {
        // Sonraki satır bir tür hatası oluşturur çünkü uint[3] belleği, uint[] belleğine dönüştürülemez.
        uint[] memory x = [uint(1), 3, 4];
    }
}

İleride bu kısıtlamanın kaldırılması planlanıyor ancak dizilerin ABI’dan geçirilme şekli nedeniyle bazı komplikasyonlar yaratıyor.

Dinamik olarak boyutlandırılmış dizileri başlatmak istiyorsanız, tek tek öğeleri atamanız gerekir:

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

contract C {
    function f() public pure {
        uint[] memory x = new uint[](3);
        x[0] = 1;
        x[1] = 3;
        x[2] = 4;
    }
}

Dizi Üyeleri

length:

Diziler, eleman sayısını içeren bir length (uzunluk) üyesine sahiptir.Bellek dizilerinin uzunluğu, oluşturulduktan sonra sabittir (ancak dinamiktir, yani çalışma zamanı parametrelerine bağlı olabilir).

push():

Dinamik depolama dizileri ve bytes (string değil), dizinin sonuna sıfır başlatılmış bir öğe eklemek için kullanabileceğiniz push() adlı üye fonksiyonuna sahiptir. Öğeye bir başvuru döndürür, böylece x.push().t = 2 veya x.push() = b gibi kullanılabilir.

push(x):

Dinamik depolama dizileri ve bytes (string değil), dizinin sonuna belirli bir öğeyi eklemek için kullanabileceğiniz push(x) adlı bir üye fonksiyonuna sahiptir. Fonksiyon hiçbir şey döndürmez.

pop():

Dinamik depolama dizileri ve bytes (string değil), dizinin sonundan bir öğeyi kaldırmak için kullanabileceğiniz pop() adlı bir üye fonksiyonuna sahiptir. Bu ayrıca kaldırılan öğede örtük olarak delete öğesini çağırır. Fonksiyon hiçbir şey döndürmez.

Not

pop() kullanarak uzunluk azaltılırken kaldırılan öğenin “boyutuna” bağlı olarak bir ücreti varken, bir depolama dizisinin uzunluğunu push() çağırarak artırmanın sabit gaz maliyetleri vardır çünkü başlarken depolama sıfırdır. Kaldırılan öğe bir diziyse, çok maliyetli olabilir, çünkü delete çağrılmasına benzer şekilde kaldırılan öğelerin açıkça temizlenmesini içerir.

Not

Dizi dizilerini harici (genel yerine) fonksiyonlarda kullanmak için ABI kodlayıcı v2’yi etkinleştirmeniz gerekir.

Not

“Byzantium” öncesi EVM sürümlerinde fonksiyon çağrılarından dönen dinamik dizilere erişim mümkün değildi. Dinamik diziler döndüren fonksiyonları çağırırsanız, Byzantium moduna ayarlanmış bir EVM kullandığınızdan emin olun.

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

contract ArrayContract {
    uint[2**20] aLotOfIntegers;
    // Aşağıdakilerin bir çift dinamik dizi değil, dinamik bir çift dizisi (yani, iki uzunluktaki sabit boyutlu diziler) olduğuna dikkat edin.
    // Bu nedenle, T[], T'nin kendisi bir dizi olsa bile, her zaman dinamik bir T dizisidir.
    // Tüm durum değişkenleri için veri konumu depolamadır.
    bool[2][] pairsOfFlags;

    // newPairs bellekte saklanır - tek olasılık
    // açık (public) sözleşme fonksiyonları argümanları için
    function setAllFlagPairs(bool[2][] memory newPairs) public {
        // bir depolama dizisine atama, "``newPairs``in bir kopyasını gerçekleştirir ve ``pairsOfFlags`` dizisinin tamamının yerini alır.
        pairsOfFlags = newPairs;
    }

    struct StructType {
        uint[] contents;
        uint moreInfo;
    }
    StructType s;

    function f(uint[] memory c) public {
        // ``g`` içindeki ``s`` referansını saklar
        StructType storage g = s;
        // ayrıca ``s.moreInfo``yu da değiştirir.
        g.moreInfo = 2;
        // ``g.contents`` yerel bir değişken değil, yerel bir değişkenin üyesi olduğu için bir kopya atar.
        g.contents = c;
    }

    function setFlagPair(uint index, bool flagA, bool flagB) public {
        // var olmayan bir dizine erişim bir istisna atar
        pairsOfFlags[index][0] = flagA;
        pairsOfFlags[index][1] = flagB;
    }

    function changeFlagArraySize(uint newSize) public {
        // bir dizinin uzunluğunu değiştirmenin tek yolu push ve pop kullanmaktır
        if (newSize < pairsOfFlags.length) {
            while (pairsOfFlags.length > newSize)
                pairsOfFlags.pop();
        } else if (newSize > pairsOfFlags.length) {
            while (pairsOfFlags.length < newSize)
                pairsOfFlags.push();
        }
    }

    function clear() public {
        // bunlar dizileri tamamen temizler
        delete pairsOfFlags;
        delete aLotOfIntegers;
        // identical effect here
        pairsOfFlags = new bool[2][](0);
    }

    bytes byteData;

    function byteArrays(bytes memory data) public {
        // bayt dizileri ("bayts"), dolgu olmadan depolandıkları için farklıdır, ancak "uint8[]" ile aynı şekilde ele alınabilirler.
        byteData = data;
        for (uint i = 0; i < 7; i++)
            byteData.push();
        byteData[3] = 0x08;
        delete byteData[2];
    }

    function addFlag(bool[2] memory flag) public returns (uint) {
        pairsOfFlags.push(flag);
        return pairsOfFlags.length;
    }

    function createMemoryArray(uint size) public pure returns (bytes memory) {
        // Dinamik bellek dizileri `new` kullanılarak oluşturulur:
        uint[2][] memory arrayOfPairs = new uint[2][](size);

        // Satır içi diziler her zaman statik olarak boyutlandırılmıştır ve yalnızca değişmez değerler kullanıyorsanız, en az bir tür sağlamanız gerekir.
        arrayOfPairs[0] = [uint(1), 2];

        // Dinamik bir bayt dizisi oluşturun:
        bytes memory b = new bytes(200);
        for (uint i = 0; i < b.length; i++)
            b[i] = bytes1(uint8(i));
        return b;
    }
}

Depolama Dizisi Öğelerine Sarkan Referanslar

Depolama dizileriyle çalışırken, sarkan referanslardan kaçınmaya özen göstermeniz gerekir. Sarkan referans, artık var olmayan veya referans güncellenmeden taşınmış bir şeye işaret eden bir referanstır. Örneğin, bir dizi öğesine bir başvuruyu yerel bir değişkende saklarsanız ve ardından içeren diziden .pop() depolarsanız, sarkan bir başvuru oluşabilir:

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

contract C {
    uint[][] s;

    function f() public {
        // s öğesinin son dizi öğesine bir işaretçi depolar.
        uint[] storage ptr = s[s.length - 1];
        // s öğesinin son dizi öğesini kaldırır.
        s.pop();
        // Artık dizi içinde olmayan dizi öğesine yazar.
        ptr.push(0x42);
        // Şimdi ``s`` öğesine yeni bir öğe eklemek boş bir dizi eklemez, ancak öğe olarak ``0x42`` olan 1 uzunluğunda bir diziyle sonuçlanır.
        s.push();
        assert(s[s.length - 1][0] == 0x42);
    }
}

ptr.push(0x42) içindeki yazma, ptr``nin artık geçerli bir ``s öğesini ifade etmemesine rağmen dönmeyecek. Derleyici kullanılmayan depolamanın her zaman sıfırlandığını varsaydığından, sonraki bir s.push(), depolamaya açıkça sıfır yazmaz, bu nedenle push()``dan sonraki ``s``nin son öğesi ``1 uzunluğa sahip ve ilk öğesi olarak 0x42 içeriyor.

Solidity’nin, depolamadaki değer türlerine referansların bildirilmesine izin vermediğini unutmayın. Bu tür açık sarkan başvurular, iç içe geçmiş başvuru türleriyle sınırlıdır. Ancak, tanımlama grubu atamalarında karmaşık ifadeler kullanılırken geçici olarak sarkan referanslar da oluşabilir:

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

contract C {
    uint[] s;
    uint[] t;
    constructor() {
        // Bazı başlangıç değerlerini depolama dizilerine aktarın.
        s.push(0x07);
        t.push(0x03);
    }

    function g() internal returns (uint[] storage) {
        s.pop();
        return t;
    }

    function f() public returns (uint[] memory) {
        // Aşağıdakiler ilk önce ``s.push()`` öğesini dizin 1'deki yeni bir öğeye yapılan bir başvuruya göre değerlendirecektir.
        // Daha sonra, ``g`` çağrısı bu yeni öğeyi açar ve en soldaki demet öğesinin sarkan bir referans haline gelmesine neden olur.
        // Atama hala devam ediyor ve ``s`` veri alanının dışına yazacak.
        (s.push(), g()[0]) = (0x42, 0x17);
        // Daha sonra ``s``ye basılması (push edilmesi/pushlanması), önceki ifade tarafından yazılan değeri ortaya çıkaracaktır, yani bu fonksiyonun sonunda "s"nin son elemanı "0x42" değerine sahip olacaktır.
        s.push();
        return s;
    }
}

Her ifade için depolamaya yalnızca bir kez atama yapmak ve atamanın sol tarafında karmaşık ifadelerden kaçınmak her zaman daha güvenlidir.

Bir bayt dizisindeki bir .push(), depolamada kısa düzenden uzun düzene geçebileceğinden, bytes dizilerinin öğelerine yapılan başvurularla uğraşırken özellikle dikkatli olmanız gerekir.

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

// Bu bir uyarı bildirir
contract C {
    bytes x = "012345678901234567890123456789";

    function test() external returns(uint) {
        (x.push(), x.push()) = (0x01, 0x02);
        return x.length;
    }
}

Burada, ilk x.push() değerlendirildiğinde, x hala kısa düzende saklanır, bu nedenle x.push(), x``in ilk depolama yuvasındaki bir öğeye bir referans döndürür. Ancak, ikinci ``x.push() bayt dizisini büyük düzene geçirir. Şimdi x.push() öğesinin atıfta bulunduğu öğe dizinin veri alanındayken, başvuru hala uzunluk alanının bir parçası olan orijinal konumunu işaret eder ve atama, x dizisinin uzunluğunu etkin bir şekilde bozar.

Güvende olmak için, tek bir atama sırasında bayt dizilerini yalnızca en fazla bir öğeyle büyütün ve aynı ifadede diziye aynı anda dizin erişimi yapmayın.

Yukarıda, derleyicinin geçerli sürümündeki sarkan depolama referanslarının davranışı açıklanırken, sarkan referanslara sahip herhangi bir kodun tanımsız davranışa sahip olduğu düşünülmelidir. Özellikle bu, derleyicinin gelecekteki herhangi bir sürümünün, sarkan referanslar içeren kodun davranışını değiştirebileceği anlamına gelir.

Kodunuzda sarkan referanslardan kaçındığınızdan emin olun!

Dizi Dilimleri

Dizi dilimleri, bir dizinin bitişik kısmındaki bir görünümdür. x[start:end] olarak yazılırlar, burada start ve end , uint256 türüyle sonuçlanan (veya dolaylı olarak ona dönüştürülebilir) ifadelerdir. Dilimin ilk öğesi x[start] ve son öğesi x[end - 1] dir.

start, end``den büyükse veya ``end, dizinin uzunluğundan büyükse, bir istisna atılır.

Hem start hem de end isteğe bağlıdır: start varsayılanları 0 ve end varsayılanları dizinin uzunluğudur.

Dizi dilimlerinin herhangi bir üyesi yoktur. Altta yatan türdeki dizilere örtük olarak dönüştürülebilirler ve dizin erişimini desteklerler. Dizin erişimi, temel alınan dizide mutlak değil, dilimin başlangıcına göredir.

Dizi dilimlerinin bir tür adı yoktur, yani hiçbir değişken tür olarak dizi dilimlerine sahip olamaz, yalnızca ara ifadelerde bulunurlar.

Not

Şu anda dizi dilimleri yalnızca çağrı verisi dizileri için uygulanmaktadır.

Dizi dilimleri, fonksiyon parametrelerinde iletilen ikincil verilerin ABI kodunu çözmek için kullanışlıdır:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.5 <0.9.0;
contract Proxy {
    /// @dev, proxy (vekil) tarafından yönetilen alıcı (client) sözleşmesinin adresi, yani bu sözleşme
    address client;

    constructor(address client_) {
        client = client_;
    }

    /// Adres bağımsız değişkeninde temel doğrulama yaptıktan sonra istemci tarafından uygulanan "setOwner(address)" çağrısını yönlendirin.
    function forward(bytes calldata payload) external {
        bytes4 sig = bytes4(payload[:4]);
        // Kesme davranışı nedeniyle, bytes4(payload) aynı şekilde çalışır.
        // bytes4 sig = bytes4(payload);
        if (sig == bytes4(keccak256("setOwner(address)"))) {
            address owner = abi.decode(payload[4:], (address));
            require(owner != address(0), "Address of owner cannot be zero.");
        }
        (bool status,) = client.delegatecall(payload);
        require(status, "Forwarded call failed.");
    }
}

Yapılar

Solidity, aşağıdaki örnekte gösterildiği gibi, yapılar biçiminde yeni türleri tanımlamanın bir yolunu sağlar:

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

// İki alanlı yeni bir tür tanımlar.
// Bir sözleşmenin dışında bir yapı bildirmek, birden fazla sözleşme tarafından paylaşılmasına izin verir.
// Burada, bu gerçekten gerekli değil.
struct Funder {
    address addr;
    uint amount;
}

contract CrowdFunding {
    // Yapılar, sözleşmelerin içinde de tanımlanabilir, bu da onları yalnızca orada ve türetilmiş sözleşmelerde görünür kılar.
    struct Campaign {
        address payable beneficiary;
        uint fundingGoal;
        uint numFunders;
        uint amount;
        mapping (uint => Funder) funders;
    }

    uint numCampaigns;
    mapping (uint => Campaign) campaigns;

    function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
        campaignID = numCampaigns++; // campaignID is return variable
        // "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)" kullanamayız çünkü sağ taraf bir eşleme içeren bir bellek yapısı "Campaign" oluşturur.
        Campaign storage c = campaigns[campaignID];
        c.beneficiary = beneficiary;
        c.fundingGoal = goal;
    }

    function contribute(uint campaignID) public payable {
        Campaign storage c = campaigns[campaignID];
        // Verilen değerlerle başlatılan yeni bir geçici bellek yapısı oluşturur ve bunu depoya kopyalar.
        // Başlatmak için Funder(msg.sender, msg.value) öğesini de kullanabileceğinizi unutmayın.
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
        c.amount += msg.value;
    }

    function checkGoalReached(uint campaignID) public returns (bool reached) {
        Campaign storage c = campaigns[campaignID];
        if (c.amount < c.fundingGoal)
            return false;
        uint amount = c.amount;
        c.amount = 0;
        c.beneficiary.transfer(amount);
        return true;
    }
}

Sözleşme, bir kitle fonlaması sözleşmesinin tam işlevselliğini sağlamaz, ancak yapıları anlamak için gerekli temel kavramları içerir. Yapı türleri eşlemeler ve diziler içinde kullanılabilir ve kendileri eşlemeler ve diziler içerebilir.

Bir yapının kendi türünden bir üye içermesi mümkün değildir, ancak yapının kendisi bir eşleme üyesinin değer türü olabilir veya kendi türünde dinamik olarak boyutlandırılmış bir dizi içerebilir. Yapının boyutunun sonlu olması gerektiğinden bu kısıtlama gereklidir.

Tüm fonksiyonlarda, veri konumu storage olan yerel bir değişkene bir yapı türünün nasıl atandığına dikkat edin. Bu, yapıyı kopyalamaz, ancak yalnızca bir referansı saklar, böylece yerel değişkenin üyelerine yapılan atamalar aslında duruma yazılır.

Not

Solidity 0.7.0’a kadar, yalnızca depolama türlerinin üyelerini (ör. eşlemeler) içeren bellek yapılarına izin veriliyordu ve campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0) gibi atamalar işe yarıyordu ve bunları sessizce atlıyordu.

Eşleme Türleri

Eşleme türleri mapping(KeyType => ValueType) sözdizimi yapısını kullanır ve eşleme türünün değişlenleri, mapping(KeyType => ValueType) VariableName sözdizimi kullanılarak bildirilir.

KeyType, herhangi bir yerleşik değer türü, bytes, string, herhangi bir sözleşme ya da numaralandırma türü olabilir. Eşlemeler, yapılar veya dizi türleri gibi diğer kullanıcı tanımlı veya karmaşık türlere izin verilmez. ValueType, eşlemeleri, dizileri ve yapıları içeren herhangi bir tür olabilir.

Eşlemeleri, olası her anahtarın var olduğu ve bir türün varsayılan değeri olan bayt temsilinin tamamı sıfır olan bir değere eşlendiği şekilde sanal olarak başlatılan karma tablolar olarak düşünebilirsiniz. Benzerlik burada sona eriyor, anahtar veriler bir eşlemede saklanmıyor, değeri aramak için yalnızca keccak256 karma değeri kullanılıyor.

Bu nedenle, eşlemelerin bir uzunluğu veya ayarlanan bir anahtar veya değer kavramı yoktur ve bu nedenle atanan anahtarlarla ilgili ek bilgi olmadan silinemezler (bkz. Mappingleri Temizleme).

Eşlemeler yalnızca storage veri konumuna sahip olabilir ve bu nedenle, fonksiyonlardaki depolama referans türleri olarak veya kitaplık fonksiyonları için parametreler olarak durum değişkenleri için izin verilir. Bunlar, genel olarak görülebilen sözleşme fonksiyonlarının parametreleri veya dönüş parametreleri olarak kullanılamazlar. Bu kısıtlamalar, eşlemeler içeren diziler ve yapılar için de geçerlidir.

Eşleme türündeki durum değişkenlerini public olarak işaretleyebilirsiniz ve Solidity sizin için bir alıcı oluşturur. KeyType, alıcı için bir parametre olur. ValueType bir değer türü veya yapıysa, alıcı ValueType değerini döndürür. ValueType bir dizi veya eşleme ise, alıcının her bir KeyType için yinelemeli olarak bir parametresi vardır.

Aşağıdaki örnekte, MappingExample sözleşmesi, anahtar türü bir address olan genel bir balances eşlemesini ve bir Ethereum adresini işaretsiz bir tamsayı değerine eşleyen bir uint değer türünü tanımlar. uint bir değer türü olduğundan, alıcı, belirtilen adreste değeri döndüren MappingUser sözleşmesinde görebileceğiniz türle eşleşen bir değer döndürür.

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

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

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(address(this));
    }
}

Aşağıdaki örnek, bir ERC20 tokenin basitleştirilmiş bir versiyonudur. _allowances, başka bir eşleme türü içindeki eşleme türüne bir örnektir.

Aşağıdaki örnekte, başka birinin hesabınızdan çekmesine izin verilen tutarı kaydetmek için _allowances kullanılmıştır.

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

contract MappingExample {

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        require(_allowances[sender][msg.sender] >= amount, "ERC20: Allowance not high enough.");
        _allowances[sender][msg.sender] -= amount;
        _transfer(sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: Not enough funds.");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }
}

Yinelenebilir Eşlemeler

Eşlemeleri yineleyemezsiniz, yani anahtarlarını numaralandıramazsınız. Yine de bunların üzerine bir veri yapısı uygulamak ve bunun üzerinde yineleme yapmak mümkündür. Örneğin, aşağıdaki kod, User sözleşmesinin daha sonra veri eklediği bir IterableMapping kitaplığı uygular ve sum fonksiyonu tüm değerleri toplamak için yinelenir.

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

struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }

struct itmap {
    mapping(uint => IndexValue) data;
    KeyFlag[] keys;
    uint size;
}

type Iterator is uint;

library IterableMapping {
    function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
        uint keyIndex = self.data[key].keyIndex;
        self.data[key].value = value;
        if (keyIndex > 0)
            return true;
        else {
            keyIndex = self.keys.length;
            self.keys.push();
            self.data[key].keyIndex = keyIndex + 1;
            self.keys[keyIndex].key = key;
            self.size++;
            return false;
        }
    }

    function remove(itmap storage self, uint key) internal returns (bool success) {
        uint keyIndex = self.data[key].keyIndex;
        if (keyIndex == 0)
            return false;
        delete self.data[key];
        self.keys[keyIndex - 1].deleted = true;
        self.size --;
    }

    function contains(itmap storage self, uint key) internal view returns (bool) {
        return self.data[key].keyIndex > 0;
    }

    function iterateStart(itmap storage self) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, 0);
    }

    function iterateValid(itmap storage self, Iterator iterator) internal view returns (bool) {
        return Iterator.unwrap(iterator) < self.keys.length;
    }

    function iterateNext(itmap storage self, Iterator iterator) internal view returns (Iterator) {
        return iteratorSkipDeleted(self, Iterator.unwrap(iterator) + 1);
    }

    function iterateGet(itmap storage self, Iterator iterator) internal view returns (uint key, uint value) {
        uint keyIndex = Iterator.unwrap(iterator);
        key = self.keys[keyIndex].key;
        value = self.data[key].value;
    }

    function iteratorSkipDeleted(itmap storage self, uint keyIndex) private view returns (Iterator) {
        while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
            keyIndex++;
        return Iterator.wrap(keyIndex);
    }
}

// Nasıl kullanılır
contract User {
    // Sadece verilerimizi tutan bir yapı.
    itmap data;
    // Veri türüne kitaplık fonksiyonlarını uygulayın.
    using IterableMapping for itmap;

    // Bir şeyleri ekle
    function insert(uint k, uint v) public returns (uint size) {
        // Bu, IterableMapping.insert(data, k, v)'yi çağırır.
        data.insert(k, v);
        // Yapının üyelerine hala erişebiliriz,
        // ama bunlarla uğraşmamaya özen göstermeliyiz.
        return data.size;
    }

    // Depolanan tüm verilerin toplamını hesaplar.
    function sum() public view returns (uint s) {
        for (
            Iterator i = data.iterateStart();
            data.iterateValid(i);
            i = data.iterateNext(i)
        ) {
            (, uint value) = data.iterateGet(i);
            s += value;
        }
    }
}

Operatörler

Aritmetik operatörler ve bit operatörleri, iki işlenen aynı türe sahip olmasa bile uygulanabilir. Örneğin, y = x + z yi hesaplayabilirsiniz, burada x bir uint8 dir ve z nin türü uint32 dir. Bu durumlarda, işlemin hesaplandığı türü (taşma durumunda bu önemlidir) ve operatörün sonucunun türünü belirlemek için aşağıdaki mekanizma kullanılacaktır:

  1. Sağ işlenenin türü dolaylı olarak sol işlenenin türüne dönüştürülebiliyorsa,

    sol işlenenin türünü kullanın.

  2. Sol işlenenin türü dolaylı olarak sağ işlenenin türüne dönüştürülebiliyorsa,

    sağ işlenenin türünü kullanın,

  3. İki seçenek de uygulanamıyorsa işleme izin verilmez.

İşlenenlerden birinin gerçek sayı olması durumunda, ilk önce değeri tutabilen en küçük tür olan “mobil türe” dönüştürülür (aynı bit genişliğindeki işaretsiz türler, işaretli türlerden “daha küçük” olarak kabul edilir) .

Her ikisi de gerçek sayıysa, işlem keyfi bir kesinlikle hesaplanır.

Operatörün sonuç türü, sonucun her zaman bool olduğu karşılaştırma operatörleri dışında, işlemin gerçekleştirildiği türle aynıdır.

** (üs alma), << ve >> operatörleri, işlem ve sonuç için sol işlenenin türünü kullanır.

Üçlü Operatör

Üçlü operatör, <expression> ? <trueExpression> : <falseExpression> formunda bulunan ifadelerin açıklanmasında kullanılır. Ana <expression> değerlendirmesinin sonucuna bağlı olarak verilen son iki ifadeden birini değerlendirir. <expression> “doğru” olarak değerlendirilirse, <trueExpression> olarak sayılır, aksi takdirde <falseExpression> olarak sayılır.

Üçlü operatörün sonucu, tüm işlenenleri rasyonel sayı değişmezleri olsa bile, bir rasyonel sayı türüne sahip değildir. Sonuç türü, iki işlenenin türlerinden yukarıdakiyle aynı şekilde belirlenir, gerekirse ilk önce mobil türlerine dönüştürülür.

Sonuç olarak, 255 + (true ? 1 : 0) işlemi, aritmetik taşma nedeniyle geri döndürülecektir (revert edilecektir). Bunun nedeni, (true ? 1 : 0) ifadesinin uint8 türünde olmasıdır.Bu, eklemenin uint8 içinde gerçekleştirilmesini zorunlu kılıyor ve 256’nın bu tür için izin verilen aralığı aşıyor.

Diğer bir sonuç da, 1.5 + 1.5 gibi bir ifadenin geçerli olduğu, ancak 1.5 + (true ? 1.5 : 2.5) olmadığıdır. Bunun nedeni, birincisinin sınırsız kesinlikle değerlendirilen rasyonel bir ifade olması ve yalnızca nihai değerinin önemli olmasıdır. İkincisi, şu anda izin verilmeyen bir kesirli rasyonel sayının bir tam sayıya dönüştürülmesini içerir.

Bileşik Operatörler ve Artırma/Azaltma Operatörleri

a bir LValue ise (yani bir değişken veya atanabilecek bir şey), aşağıdaki operatörler kısayol olarak kullanılabilir:

a += e, a = a + e ile eşdeğerdir. -=, *=, /=, %=, |=, &=, ^=, <<= ve >>= buna göre tanımlanır. a++ ve a--, a += 1 / a -= 1 ile eşdeğerdir, ancak ifadenin kendisi hala önceki a değerine sahiptir. Buna karşılık, --a ve ++a, a üzerinde aynı etkiye sahiptir ancak değişiklikten sonra değeri döndürür.

silmek

delete a, türün başlangıç değerini a``ya atar. Yani, tamsayılar için ``a = 0 ile eşdeğerdir, ancak sıfır uzunlukta dinamik bir dizi veya tüm öğeleri başlangıç değerlerine ayarlanmış aynı uzunlukta statik bir dizi atadığı dizilerde de kullanılabilir.

delete a[x], dizinin x dizinindeki öğeyi siler ve diğer tüm öğelere ve dizinin uzunluğuna dokunmadan bırakır. Bu özellikle dizide bir boşluk bırakıldığı anlamına gelir. Öğeleri kaldırmayı planlıyorsanız, eşleme yapmak muhtemelen daha iyi bir seçimdir.

Yapılar (structs) için, tüm üyelerin sıfırlandığı bir yapı atar. Başka bir deyişle, a nın delete a dan sonraki değeri, a nın atama olmadan bildirilmesiyle aynıdır:

delete fonksiyonunun eşlemeler üzerinde hiçbir etkisi yoktur (çünkü eşlemelerin anahtarları rastgele olabilir ve genellikle bilinmez). Bu nedenle, bir yapıyı silerseniz, eşleme olmayan tüm üyeleri sıfırlar ve eşleme olmadıkça üyelere geri döner. Ancak, bireysel anahtarlar ve eşledikleri şey silinebilir: a bir eşleme ise, delete a[x] , x de depolanan değeri siler.

delete a nın gerçekten a ya atanmış gibi davrandığını, yani a da yeni bir nesne depoladığını unutmamak önemlidir. Bu ayrım, a referans değişkeni olduğunda görünür: Daha önce atıfta bulunduğu değeri değil, yalnızca a nın kendisini sıfırlayacaktır.

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

contract DeleteExample {
    uint data;
    uint[] dataArray;

    function f() public {
        uint x = data;
        delete x; // x'i 0'a ayarlar, verileri etkilemez
        delete data; // verileri 0'a ayarlar, x'i etkilemez
        uint[] storage y = dataArray;
        delete dataArray; // bu, dataArray.length değerini sıfıra ayarlar, ancak uint[] karmaşık bir nesne olduğundan,
        // depolama nesnesinin diğer adı olan y da etkilenir.
        // Öte yandan: "delete y" geçerli değildir, çünkü depolama nesnelerine başvuran yerel değişkenlere atamalar yalnızca mevcut depolama nesnelerinden yapılabilir.
        assert(y.length == 0);
    }
}

Operatörlerin Öncelik Sırası

Aşağıdaki tablo, değerlendirme sırasına göre listelenen operatörler için öncelik sırasını belirtir.

Öncelik

Tanım

Operatör

1

Son ek ile tırma ve azaltma

++, --

Yeni ifade

new <typename>

Dizi elamanı görüntüleme

<array>[<index>]

Üye erişimi

<object>.<member>

Fonksiyon çağırımı

<func>(<args...>)

Parantezler

(<statement>)

2

Ön ek ile artırma ve azaltma

++, --

Tekli çıkarma

-

Tekli işlemler

delete

Mantıksal ‘DEĞİL’

!

Bitsel ‘DEĞİL’

~

3

Üs alma

**

4

Çarpma, bölme ve mod alma

*, /, %

5

Ekleme ve çıkarma

+, -

6

Bitsel değiştirme operatörleri

<<, >>

7

Bitsel ‘VE’

&

8

Bitsel ‘Özel veya’

^

9

Bitsel ‘YA DA’

|

10

Eşitsizlik operatörleri

<, >, <=, >=

11

Eşitlik operatörleri

==, !=

12

Mantıksal ‘VE’

&&

13

Mantıksal ‘YA DA’

||

14

Üçlü operatör

<conditional> ? <if-true> : <if-false>

Atama operatörleri

=, |=, ^=, &=, <<=, >>=, +=, -=, *=, /=, %=

15

Virgül operatörü

,

Temel Türler Arası Dönüşümler

Örtülü Dönüşümler

Örtülü tür dönüşümü, argümanları fonksiyonlara iletme ya da operatör atamaları sırasında, derleyici tarafından otomatik olarak uygulanır. Genel olarak, bilgi kaybı yoksa ve anlamsal açıdan bir sorun yoksa, değer türleri arasında örtülü bir dönüşüm mümkündür.

Örneğin, uint8 türü, uint16 türüne ve int128 türü, int256 türüne dönüştürülebilirken, int8 türü uint256 türüne dönüştürülemez çünkü uint256, -1 gibi değerleri tutamaz.

Bir operatör birbirinden farklı türlere uygulanırsa, derleyici işlenenlerden birini örtük olarak diğerinin türüne dönüştürmeye çalışır (aynısı atamalar için de geçerlidir). Bu, işlemlerin her zaman işlenenlerden birinin türünde gerçekleştirildiği anlamına gelir.

Hangi örtük dönüşümlerin mümkün olduğu hakkında daha fazla ayrıntı için, lütfen türlerle ilgili bölümlere bakın.

Aşağıdaki örnekte, toplamanın işlenenleri olarak y ve z, aynı türe sahip değildir, fakat uint8 örtük olarak uint16 türüne dönüştürülebilirken bunun tersi mümkün değildir. Bu sebeple, uint16 türünde bir dönüştürme yapılmadan önce y türü, z türüne dönüştürülür. y + z ifadesinden elde edilen tür, uint16 dır. Toplama işleminin sonucu uint32 türünde bir değişkene atandığı için, toplama işleminden sonra yeniden örtük dönüştürme gerçekleşir.

uint8 y;
uint16 z;
uint32 x = y + z;

Açık Dönüşümler

Derleyici örtük dönüştürmeye izin vermiyorsa ancak bir dönüştürmenin işe yarayacağından eminseniz, bazen açık bir tür dönüştürme mümkündür. Bu, beklenmeyen davranışlara neden olabilir ve derleyicinin bazı güvenlik özelliklerini atlamanıza izin verir, bu nedenle sonucun istediğiniz ve beklediğiniz gibi olduğunu test ettiğinizden emin olun!

Negatif değere sahip bir int değişkenini, uint değişkenine dönüştüren aşağıdaki örneği ele alalım:

int  y = -3;
uint x = uint(y);

Bu kod bloğunun sonunda x, 0xfffff..fd (64 adet onaltılık karaker) değerine sahip olacaktır, bu, iki’nin 256 bitlik tümleyen (two’s complement) temsili olan -3’tür.

Bir tam sayı, kendisinden daha küçük bir türe açık şekilde dönüştürülürse, daha yüksek dereceli bitler kesilir.

uint32 a = 0x12345678;
uint16 b = uint16(a); // b, 0x5678 olacaktır

Bir tam sayı, kendisinden daha büyük bir türe açık şekilde dönüştürülürse, elde edilen ortak tümleyenin solu yani daha yüksek dereceli ucu doldurulur. Dönüşümün sonucu orijinal tam sayıya eşit olacaktır:

uint16 a = 0x1234;
uint32 b = uint32(a); // b, 0x00001234 olacaktır
assert(a == b);

Sabit boyutlu bayt dizisi türleri, dönüşümler sırasında farklı davranır. Bireysel bayt dizileri olarak düşünülebilirler ve daha küçük bir türe dönüştürmek diziyi kesecektir:

bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b, 0x12 olacaktır

Sabit boyutlu bir bayt dizisi türü, daha büyük bir türe açıkça dönüştürülürse, elde edilen ortak tümleyen sağ tarafta doldurulur. Sabit bir dizindeki bayt dizisine erişmek, dönüştürmeden önce ve sonra aynı değerle sonuçlanır (dizin hala aralıktaysa):

bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b, 0x12340000 olacaktır
assert(a[0] == b[0]);
assert(a[1] == b[1]);

Tamsayılar ve sabit boyutlu bayt dizileri, kesme veya doldurma sırasında farklı davrandığından, tamsayılar ve sabit boyutlu bayt dizileri arasındaki açık dönüştürmelere yalnızca, her ikisi de aynı boyuta sahipse izin verilir. Farklı boyuttaki tamsayılar ve sabit boyutlu bayt dizileri arasında dönüştürmek istiyorsanız, istenen kesme ve doldurma kurallarını açık hale getiren ara dönüşümleri kullanmanız gerekir:

bytes2 a = 0x1234;
uint32 b = uint16(a); // b, 0x00001234 olacaktır
uint32 c = uint32(bytes4(a)); // c, 0x12340000 olacaktır
uint8 d = uint8(uint16(a)); // d, 0x34 olacaktır
uint8 e = uint8(bytes1(a)); // e, 0x12 olacaktır

bytes dizileri ve bytes çağrı verisi (calldata) dilimleri, sabit bayt türlerine(bytes1/…/bytes32) açıkça dönüştürülebilir. Dizinin hedef sabit bayt türünden daha uzun olması durumunda, sonunda kesme gerçekleşir. Dizi hedef türden daha kısaysa, sonunda sıfırlarla doldurulur.

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

contract C {
    bytes s = "abcdefgh";
    function f(bytes calldata c, bytes memory m) public view returns (bytes16, bytes3) {
        require(c.length == 16, "");
        bytes16 b = bytes16(m);  // 'm'in uzunluğu 16'dan büyükse, kesme gerçekleşecektir
        b = bytes16(s);  // sağa genişletilir, sonuç "abcdefgh\0\0\0\0\0\0\0\0" olacaktır
        bytes3 b1 = bytes3(s); // kesilir, b1, "abc"ye eşittir
        b = bytes16(c[:8]);  // sıfırlar ile genişletilir
        return (b, b1);
    }
}

İfadeler (Literals) ve Temel Türler Arasındaki Dönüşümler

Tamsayı Türleri

Ondalık ve onaltılık sayı ifadeleri, onu kesmeden temsil edecek kadar büyük herhangi bir tamsayı türüne örtük olarak dönüştürülebilir:

uint8 a = 12; // uygun
uint32 b = 1234; // uygun
uint16 c = 0x123456; // hatalı, çünkü 0x3456 olacak şekilde kesilmek zorundadır

Not

0.8.0 sürümünden önce, herhangi bir ondalık veya onaltılık sayı ifadeleri bir tamsayı türüne açıkça dönüştürülebilirdi. 0.8.0’dan itibaren, bu tür açık dönüştürmeler, örtülü dönüştürmeler kadar katıdır, yani, yalnızca ifade elde edilen aralığa uyuyorsa bunlara izin verilir.

Sabit Boyutlu Bayt Dizileri

Ondalık sayı ifadeleri örtük olarak sabit boyutlu bayt dizilerine dönüştürülemez. Onaltılık sayı ifadeleri olabilir, ancak yalnızca onaltılık basamak sayısı bayt türünün boyutuna tam olarak uyuyorsa. Bir istisna olarak, sıfır değerine sahip hem ondalık hem de onaltılık ifadeler herhangi bir sabit boyutlu bayt türüne dönüştürülebilir:

bytes2 a = 54321; // izin verilmez
bytes2 b = 0x12; //  izin verilmez
bytes2 c = 0x123; // izin verilmez
bytes2 d = 0x1234; // uygun
bytes2 e = 0x0012; // uygun
bytes4 f = 0; // uygun
bytes4 g = 0x0; // uygun

String ifadeleri ve onaltılı string ifadeleri, karakter sayıları bayt türünün boyutuyla eşleşiyorsa, örtük olarak sabit boyutlu bayt dizilerine dönüştürülebilir:

bytes2 a = hex"1234"; // uygun
bytes2 b = "xy"; // uygun
bytes2 c = hex"12"; // izin verilmez
bytes2 d = hex"123"; // izin verilmez
bytes2 e = "x"; // izin verilmez
bytes2 f = "xyz"; // izin verilmez

Adresler

Adres Değişmezleri bölümünde açıklandığı gibi, sağlama toplamı (checksum) testini geçen doğru boyuttaki onaltılık ifadeler address türündedir. Başka hiçbir ifade address türüne örtük olarak dönüştürülemez.

Not

0.8.0 sürümünden önce address veya address payable’a herhangi bir tamsayı türünden (herhangi bir boyutta, imzalı veya imzasız) açıkça dönüştürülebilmekteydi. 0.8.0 ile birlikte yalnızce uint160’dan dönüştürmeye izin verilmektedir.