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çinx*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 olaraktransfer
vesend
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 da0x111122223333444455556666777788889999aAaa
ile sonuçlanır,veya
0x777788889999AaAAbBbbCccccddDdeeeEfFFfCcCc
ile sonuçlananaddress(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
andtransfer
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
vestaticcall
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
andcodehash
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çinx[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
venon-payable
fonksiyonlara dönüştürülebilirview
fonksiyonlarınon-payable
fonksiyonlara dönüştürülebilirpayable
fonksiyonlarnon-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
vememory
(veyacalldata
) 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ğinizpush()
adlı üye fonksiyonuna sahiptir. Öğeye bir başvuru döndürür, böylecex.push().t = 2
veyax.push() = b
gibi kullanılabilir.- push(x):
Dinamik depolama dizileri ve
bytes
(string
değil), dizinin sonuna belirli bir öğeyi eklemek için kullanabileceğinizpush(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ğinizpop()
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:
- 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.
- 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,
İ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 |
|
|
Dizi elamanı görüntüleme |
|
|
Üye erişimi |
|
|
Fonksiyon çağırımı |
|
|
Parantezler |
|
|
2 |
Ön ek ile artırma ve azaltma |
|
Tekli çıkarma |
|
|
Tekli işlemler |
|
|
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 |
|
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 ifadeaddress
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.