Solidity v0.5.0 İşleyişi Bozan Değişiklikler
Bu bölüm, Solidity 0.5.0 sürümünde getirilen değişikliklerin, eski sürümlerdeki ana işleyişi bozan kısımlarını değişikliklerin arkasındaki gerekçeleri ve etkilenen kodun nasıl güncelleneceğini vurgular. Tam liste için sürüm değişiklik günlüğü adresini kontrol edin.
Not
Solidity v0.5.0 ile derlenen sözleşmeler, eski sürümlerle derlenen sözleşmelerle ve hatta kütüphanelerle yeniden derlenmeden veya yeniden dağıtılmadan arayüz oluşturmaya devam edebilir. Arayüzleri veri konumlarını, görünürlük ve değişebilirlik belirleyicilerini içerecek şekilde değiştirmek yeterlidir. Aşağıdaki Interoperability With Older Contracts bölümüne bakınız.
Yalnızca Anlamsal (Semantik) Değişiklikleri
Bu bölümde yalnızca semantik olan, dolayısıyla mevcut kodda yeni ve farklı davranışları gizleme potansiyeli olan değişiklikler listelenmektedir.
İşaretli sağa kaydırma artık uygun aritmetik kaydırma kullanır, yani sıfıra doğru yuvarlamak yerine negatif sonsuza doğru yuvarlar. İşaretli ve işaretsiz kaydırma Constantinople’da özel işlem kodlarına sahip olacak ve şu an için Solidity tarafından taklit edilmektedir.
Bir
do...while
döngüsündekicontinue
deyimi artık bu tür durumlarda yaygın davranış olan koşula atlıyor. Eskiden döngü gövdesine atlıyordu. Böylece, koşul yanlışsa, döngü sonlandırılır..call()
,.delegatecall()
ve.staticcall()
fonksiyonları, tek birbytes
parametresi verildiğinde artık dolgu yapmıyor.Pure ve view fonksiyonları artık EVM sürümü Byzantium veya üstü ise
CALL
yerineSTATICCALL
opcode`u kullanılarak çağrılmaktadır. Bu, EVM düzeyinde durum değişikliklerine izin vermez.ABI kodlayıcı artık harici fonksiyon çağrılarında ve
abi.encode
içinde kullanıldığında çağrı verilerinden (msg.data
ve harici fonksiyon parametreleri) bayt dizilerini ve dizeleri düzgün bir şekilde doldurur. Dolgusuz kodlama içinabi.encodePacked
kullanın.ABI dekoderi, fonksiyonların başında ve
abi.decode()
içinde, aktarılan calldata çok kısaysa veya sınırların dışına işaret ediyorsa geri döner. Yüksek dereceli kirli bitlerin hala basitçe göz ardı edildiğini unutmayın.Tangerine Whistle’dan başlayarak harici fonksiyon çağrıları ile mevcut tüm gazı iletin.
Semantik ve Sentaktik Değişiklikler
Bu bölümde sözdizimi ve anlambilimi etkileyen değişiklikler vurgulanmaktadır.
.call()
,.delegatecall()
,staticcall()
,keccak256()
,sha256()
veripemd160()
fonksiyonları artık sadece tek birbytes
argümanı kabul etmektedir. Ayrıca, argüman doldurulmamıştır. Bu, argümanların nasıl birleştirildiğini daha açık ve net hale getirmek için değiştirildi. Her.call()
(ve ailesi).call("")
olarak ve her.call(signature, a, b, c)
.call(abi.encodeWithSignature(signature, a, b, c))
olarak değiştirildi (sonuncusu yalnızca değer türleri için çalışır). Herkeccak256(a, b, c)
ifadesinikeccak256(abi.encodePacked(a, b, c))
olarak değiştirin. İşleyişi bozan bir değişiklik olmasa da, geliştiricilerinx.call(bytes4(keccak256("f(uint256)"), a, b)
öğesinix.call(abi.encodeWithSignature("f(uint256)", a, b))
olarak değiştirmeleri önerilir.Geri dönüş verilerine erişim sağlamak için
.call()
,.delegatecall()
ve.staticcall()
fonksiyonları artık(bool, bytes memory)
döndürmektedir.bool success = otherContract.call("f")
ifadesini(bool success, bytes memory data) = otherContract.call("f")
olarak değiştirin.Solidity artık fonksiyon yerel değişkenleri için C99 tarzı kapsam kurallarını uygulamaktadır, yani değişkenler yalnızca bildirildikten sonra ve yalnızca aynı veya iç içe kapsamlarda kullanılabilir. Bir
for
döngüsünün başlatma bloğunda bildirilen değişkenler, döngü içindeki herhangi bir noktada geçerlidir.
Açıklık Gereksinimleri
Bu bölüm, kodun artık daha açık olması gereken değişiklikleri listeler. Konuların çoğu için derleyici öneriler sağlayacaktır.
Açık fonksiyon görünürlüğü artık zorunludur. Her fonksiyona ve constructor’a
public
ve görünürlüğünü zaten belirtmeyen her fallback veya arayüz fonksiyonunaexternal
ekleyin.struct, array veya mapping türlerindeki tüm değişkenler için açık veri konumu artık zorunludur. Bu aynı zamanda fonksiyon parametrelerine ve dönüş değişkenlerine de uygulanır. Örneğin,
uint[] x = z
ifadesiniuint[] storage x = z
olarak vefunction f(uint[][] x)
ifadesinifunction f(uint[][] memory x)
olarak değiştirin; buradamemory
veri konumudur ve uygun şekildestorage
veyacalldata
ile değiştirilebilir.external
fonksiyonlarınıncalldata
veri konumuna sahip parametreler gerektirdiğini unutmayın.Sözleşme türleri, isim alanlarını ayırmak için artık
address
üyelerini içermemektedir. Bu nedenle, artık biraddress
üyesini kullanmadan önce sözleşme türünün değerlerini açıkça adreslere dönüştürmek gerekmektedir. Örnek:c
bir sözleşme ise,c.transfer(...)
değeriniaddress(c).transfer(...)
olarak vec.balance
değeriniaddress(c).balance
olarak değiştirin.İlişkisiz sözleşme türleri arasında açık dönüşümlere artık izin verilmemektedir. Bir sözleşme türünden yalnızca temel veya ata türlerinden birine dönüştürebilirsiniz. Bir sözleşmenin, miras almamasına rağmen dönüştürmek istediğiniz sözleşme türüyle uyumlu olduğundan eminseniz, önce
address
türüne dönüştürerek bunu aşabilirsiniz. Örnek:A
veB
sözleşme türleriyse,B
A
türünden miras almıyorsa veb
B
türünde bir sözleşmeyse,A(adres(b))
kullanarakb
türünüA
türüne dönüştürebilirsiniz. Aşağıda açıklandığı gibi, eşleşen payable fallback fonksiyonlarına dikkat etmeniz gerektiğini unutmayın.address
türüaddress
veaddress payable
olarak ikiye ayrılmıştır, burada sadeceaddress payable
transfer
fonksiyonunu sağlamaktadır. Biraddress payable
doğrudan biraddress
e dönüştürülebilir, ancak bunun tersine izin verilmez.address
’iaddress payable
’a dönüştürmekuint160
vasıtasıyla dönüşüm yoluyla mümkündür. Eğerc
bir sözleşme ise,address(c)
sadecec
bir payable fallback fonksiyonuna sahipseaddress payable
ile sonuçlanır. Eğer withdraw pattern kullanıyorsanız, büyük olasılıkla kodunuzu değiştirmeniz gerekmez çünkütransfer
saklanan adresler yerine sadecemsg.sender
üzerinde kullanılır vemsg.sender
biraddress payable
dır.Farklı boyuttaki
bytesX
veuintY
arasındaki dönüşümler, sağdakibytesX
dolgusu ve soldakiuintY
dolgusu nedeniyle artık izin verilmiyor ve bu da beklenmedik dönüşüm sonuçlarına neden olabilir. Boyut artık dönüştürmeden önce tür içinde ayarlanmalıdır. Örneğin,bytes4
(4 bayt) değişkenini öncebytes8
değişkenine ve ardındanuint64
değişkenine dönüştürerek birbytes4
(4 bayt) değişkenini biruint64
(8 bayt) değişkenine dönüştürebilirsiniz.uint32
üzerinden dönüştürme yaparken ters dolgu elde edersiniz. v0.5.0`dan öncebytesX
veuintY
arasındaki herhangi bir dönüşümuint8X
üzerinden giderdi. Örneğinuint8(bytes3(0x291807))
,uint8(uint24(bytes3(0x291807)))
’e dönüştürülürdü (sonuç0x07
dir).Payable olmayan fonksiyonlarda
msg.value
kullanımına (veya bir modifier aracılığıyla tanıtılmasına) güvenlik özelliği olarak izin verilmez. Fonksiyonupayable
haline getirin veyamsg.value
kullanan program mantığı için yeni bir dahili fonksiyon oluşturun.Anlaşılabilirlik nedeniyle, standart girdi kaynak olarak kullanıldığında komut satırı arayüzü artık
-
gerektirmektedir. Translated with www.DeepL.com/Translator (free version)
Kullanımdan Kaldırılan Öğeler
Bu bölümde, önceki özellikleri veya sözdizimini kullanımdan kaldıran değişiklikler listelenmektedir. Bu değişikliklerin çoğunun v0.5.0
deneysel modunda zaten etkin olduğunu unutmayın.
Komut Satırı ve JSON Arayüzleri
Komut satırı seçeneği
--formal
(daha fazla biçimsel doğrulama için Why3 çıktısı oluşturmak için kullanılır) kullanımdan kaldırılmıştır ve artık silinmektedir. Yeni bir biçimsel doğrulama modülü olan SMTChecker,pragma experimental SMTChecker;
ile etkinleştirilmiştir.Komut satırı seçeneği
--julia
, ara dilJulia``nın ``Yul
olarak yeniden adlandırılması nedeniyle--yul
olarak yeniden adlandırıldı.--clone-bin
ve--combined-json clone-bin
komut satırı seçenekleri kaldırıldı.Boş önek içeren yeniden eşlemelere izin verilmiyor.
JSON AST alanları
constant
vepayable
kaldırıldı. Bu bilgiler artıkstateMutability
alanında bulunmaktadır.FunctionDefinition
node’unun JSON AST alanıisConstructor
,"constructor"
,"fallback"
veya"function"
değerine sahip olabilenkind
adlı bir alanla değiştirildi.Bağlantısız ikili hex dosyalarında, kütüphane adres yer tutucuları artık
$...$
ile çevrelenmiş tam nitelikli kütüphane adının keccak256 hash’inin ilk 36 hex karakteridir. Önceden, sadece tam nitelikli kütüphane adı kullanılıyordu. Bu, özellikle uzun yollar kullanıldığında çakışma olasılığını azaltır. Binary dosyalar artık bu yer tutuculardan tam nitelikli adlara bir eşleme listesi de içeriyor.
Constructor’lar
Constructor’lar artık
constructor
anahtar sözcüğü kullanılarak tanımlanmalıdır.Temel constructor’ların parantezler olmadan çağrılmasına artık izin verilmemektedir.
Aynı kalıtım hiyerarşisinde temel constructor argümanlarının birden fazla kez belirtilmesine artık izin verilmemektedir.
Argümanları olan ancak argüman sayısı yanlış olan bir constructor çağrılmasına artık izin verilmemektedir. Argüman vermeden yalnızca bir kalıtım ilişkisi belirtmek istiyorsanız, parantezleri hiç sağlamayın.
Fonksiyonlar
Fonksiyon
callcode
artık izin verilmiyor (delegatecall
lehine). Inline assembly ile kullanmak hala mümkündür.suicide
artık izin verilmiyor (selfdestruct
lehine).sha3
artık izin verilmiyor (keccak256
lehine).throw
artık izin verilmiyor (revert
,require
veassert
lehine).
Dönüşümler
Ondalık değişmezlerden
bytesXX
türlerine açık ve örtük dönüşümlere artık izin verilmiyor.Onaltılık değişmezlerden farklı boyuttaki
bytesXX
türlerine açık ve örtük dönüşümlere artık izin verilmiyor.
Literaller ve Sonekler
Artık yıllarla ilgili karmaşıklıklar ve karışıklıklar nedeniyle
years
birim gösterimine artık izin verilmemektedir.Bir sayı tarafından takip edilmeyen sondaki noktalara artık izin verilmemektedir.
Onaltılık sayıların birim değerleriyle birleştirilmesine (örneğin
0x1e wei
) artık izin verilmemektedir.Onaltılık sayılar için
0X
önekine izin verilmez, sadece0x
mümkündür.
Değişkenler
Anlaşılabilirlik için boş structların tanımlanmasına artık izin verilmiyor.
var
anahtar sözcüğüne artık netlik için izin verilmiyor.Farklı sayıda bileşene sahip tuple’lar arasındaki atamalara artık izin verilmiyor.
Derleme zamanı sabitleri olmayan sabitler için değerlere izin verilmez.
Uyumsuz sayıda değere sahip çok değişkenli bildirimlere artık izin verilmemektedir.
Başlatılmamış depolama değişkenlerine artık izin verilmemektedir.
Boş tuple bileşenlerine artık izin verilmiyor.
Değişkenler ve struct’lardaki döngüsel bağımlılıkların algılanması özyinelemede 256 ile sınırlandırılmıştır.
Uzunluğu sıfır olan sabit boyutlu dizilere artık izin verilmemektedir.
Sözdizimi
Fonksiyon durumu değişebilirlik değiştiricisi olarak
constant
kullanımına artık izin verilmemektedir.Boolean ifadeler aritmetik işlemler kullanamaz.
Unary
+
operatörüne artık izin verilmiyor.Harfler artık önceden açık bir türe dönüştürülmeden
abi.encodePacked
ile kullanılamaz.Bir veya daha fazla dönüş değeri olan fonksiyonlar için boş dönüş ifadelerine artık izin verilmemektedir.
” loose assembly” sözdizimine artık tamamen izin verilmiyor, yani atlama etiketleri, atlamalar ve işlevsel olmayan talimatlar artık kullanılamaz. Bunun yerine yeni
while
,switch
veif
yapılarını kullanın.Uygulaması olmayan fonksiyonlar artık modifier kullanamaz.
Adlandırılmış dönüş değerlerine sahip fonksiyon tiplerine artık izin verilmemektedir.
Blok olmayan if/while/for gövdeleri içindeki tek deyimli değişken bildirimlerine artık izin verilmiyor.
Yeni anahtar kelimeler:
calldata
veconstructor
.Yeni ayrılmış anahtar sözcükler:
alias
,apply
,auto
,copyof
,define
,immutable
,implements
,macro
,mutable
,override
,partial
,promise
,reference
,sealed
,sizeof
,supports
,typedef
veunchecked
.
Eski Sözleşmelerle Birlikte Çalışabilirlik
Solidity’nin v0.5.0’dan önceki sürümleri için yazılmış sözleşmeler için arayüzler tanımlayarak (veya tam tersi şekilde) arayüz oluşturmak hala mümkündür. Aşağıdaki 0.5.0 öncesi sözleşmenin zaten dağıtılmış olduğunu düşünün:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.25;
// Bu, derleyicinin 0.4.25 sürümüne kadar bir uyarı bildirecektir
// Bu 0.5.0'dan sonra derlenmeyecektir
contract OldContract {
function someOldFunction(uint8 a) {
//...
}
function anotherOldFunction() constant returns (bool) {
//...
}
// ...
}
Bu artık Solidity v0.5.0 ile derlenmeyecektir. Ancak, bunun için uyumlu bir arayüz tanımlayabilirsiniz:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
interface OldContract {
function someOldFunction(uint8 a) external;
function anotherOldFunction() external returns (bool);
}
Orijinal sözleşmede constant
olarak tanımlanmasına rağmen anotherOldFunction
fonksiyonunu view
olarak tanımlamadığımıza dikkat edin. Bunun nedeni Solidity v0.5.0`dan
itibaren view
fonksiyonlarını çağırmak için staticcall
kullanılmasıdır. v0.5.0 öncesinde
constant
anahtar sözcüğü zorunlu değildi, bu nedenle constant
olarak bildirilen bir
fonksiyonu staticcall
ile çağırmak yine de geri dönebilir, çünkü constant
fonksiyonu
hala depolamayı değiştirmeye çalışabilir. Sonuç olarak, eski sözleşmeler için bir arayüz
tanımlarken, constant
yerine sadece fonksiyonun staticcall
ile çalışacağından kesinlikle
emin olduğunuz durumlarda view
kullanmalısınız.
Yukarıda tanımlanan arayüz göz önüne alındığında, artık halihazırda dağıtılmış olan 0.5.0 öncesi sözleşmeyi kolayca kullanabilirsiniz:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
interface OldContract {
function someOldFunction(uint8 a) external;
function anotherOldFunction() external returns (bool);
}
contract NewContract {
function doSomething(OldContract a) public returns (bool) {
a.someOldFunction(0x42);
return a.anotherOldFunction();
}
}
Benzer şekilde, 0.5.0 öncesi kütüphaneler, kütüphanenin fonksiyonları uygulanmadan tanımlanarak ve linking sırasında 0.5.0 öncesi kütüphanenin adresi verilerek kullanılabilir (linking için komut satırı derleyicisinin nasıl kullanılacağını öğrenmek için Komut Satırı Derleyicisinin Kullanımı bölümüne bakınız):
// This will not compile after 0.6.0
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.5.0;
library OldLibrary {
function someFunction(uint8 a) public returns(bool);
}
contract NewContract {
function f(uint8 a) public returns (bool) {
return OldLibrary.someFunction(a);
}
}
Örnek
Aşağıdaki örnekte bir sözleşme ve bu bölümde listelenen bazı değişikliklerle Solidity v0.5.0 için güncellenmiş sürümü gösterilmektedir.
Eski versiyon:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.25;
// Bu 0.5.0'dan sonra derlenmeyecektir
contract OtherContract {
uint x;
function f(uint y) external {
x = y;
}
function() payable external {}
}
contract Old {
OtherContract other;
uint myNumber;
// Fonksiyon değişebilirliği sağlanmadı, hata değil.
function someInteger() internal returns (uint) { return 2; }
// Fonksiyon görünürlüğü sağlanmadı, hata değil.
// Fonksiyon değişebilirliği sağlanmadı, hata değil.
function f(uint x) returns (bytes) {
// Var bu versiyonda sorunsuz çalışıyor.
var z = someInteger();
x += z;
// Throw bu versiyonda sorunsuz çalışıyor.
if (x > 100)
throw;
bytes memory b = new bytes(x);
y = -3 >> 1;
// y == -1 (yanlış, -2 olmalı)
do {
x += 1;
if (x > 10) continue;
// 'Continue' sonsuz döngüye neden olur.
} while (x < 11);
// Çağrı yalnızca bir Bool döndürür.
bool success = address(other).call("f");
if (!success)
revert();
else {
// Yerel değişkenler kullanımlarından sonra bildirilebilir.
int y;
}
return b;
}
// 'arr' için açık bir veri konumuna gerek yok
function g(uint[] arr, bytes8 x, OtherContract otherContract) public {
otherContract.transfer(1 ether);
// uint32 (4 bayt) bytes8'den (8 bayt) daha küçük olduğundan,
// x'in ilk 4 baytı kaybolacaktır. Bu durum, bytesX sağa doğru
// doldurulduğundan beklenmedik davranışlara yol açabilir.
uint32 y = uint32(x);
myNumber += y + msg.value;
}
}
Yeni versiyon:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.5.0;
// Bu 0.6.0'dan sonra derlenmeyecektir
contract OtherContract {
uint x;
function f(uint y) external {
x = y;
}
function() payable external {}
}
contract New {
OtherContract other;
uint myNumber;
// Fonksiyon değişebilirliği belirtilmelidir.
function someInteger() internal pure returns (uint) { return 2; }
// Fonksiyon görünürlüğü belirtilmelidir.
// Fonksiyon değişebilirliği belirtilmelidir.
function f(uint x) public returns (bytes memory) {
// Tür şimdi açıkça verilmelidir.
uint z = someInteger();
x += z;
// Throw'a artık izin verilmiyor.
require(x <= 100);
int y = -3 >> 1;
require(y == -2);
do {
x += 1;
if (x > 10) continue;
// 'Continue' ile aşağıdaki koşula atlanır.
} while (x < 11);
// Çağrı (bool, bayt) döndürür.
// Veri konumu belirtilmelidir.
(bool success, bytes memory data) = address(other).call("f");
if (!success)
revert();
return data;
}
using AddressMakePayable for address;
// 'arr' için veri konumu belirtilmelidir
function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
// 'otherContract.transfer' sağlanmamıştır.
// 'OtherContract' kodu bilindiğinden ve fallback fonksiyonuna sahip olduğundan,
// address(otherContract) 'address payable' tipine sahiptir.
address(otherContract).transfer(1 ether);
// 'unknownContract.transfer' sağlanmadı.
// 'address(unknownContract).transfer',
// 'address(unknownContract)' 'address payable' olmadığı için sağlanmamıştır.
// Fonksiyon para göndermek istediğiniz bir 'address' alırsa,
// bunu 'uint160' aracılığıyla 'address payable'a dönüştürebilirsiniz.
// Not: Bu tavsiye edilmez ve mümkün olduğunda açık
// 'address payable' türü kullanılmalıdır.
// Anlaşılabilirliği artırmak için, dönüşüm işleminde bir
// kütüphane kullanılmasını öneriyoruz (bu örnekte sözleşmeden sonra verilmiştir).
address payable addr = unknownContract.makePayable();
require(addr.send(1 ether));
// uint32 (4 bayt), bytes8'den (8 bayt) daha küçük
// olduğu için dönüştürmeye izin verilmez.
// Önce ortak bir boyuta dönüştürmemiz gerekiyor:
bytes4 x4 = bytes4(x); // Dolgu sağ tarafta gerçekleşir
uint32 y = uint32(x4); // Dönüşüm tutarlıdır
// 'msg.value' bir 'non-payable' fonksiyonunda kullanılamaz.
// Fonksiyonu ödenebilir hale getirmemiz gerekiyor
myNumber += y + msg.value;
}
}
// Geçici bir çözüm olarak ``address`` i açıkça
// ``address payable`` a dönüştürmek için bir kütüphane tanımlayabiliriz.
library AddressMakePayable {
function makePayable(address x) internal pure returns (address payable) {
return address(uint160(x));
}
}