References
Variables & Scopes
We can divide variables in Solidity into three classes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
// 1. Fixed Size Types
bool isTrue = true;
int8 number1 = 12;
uint number2 = 12;
address address = 0x.....;
bytes32 name1 = "name1";
// 2. Dynamic Size Types
string name2 = "name2";
bytes name1 = "name1";
uint[] array = [1,2,3,4,5];
mapping(uint => string) list1;
list1[3] = "value3";
mapping(uint => address) list2;
list2[4] = address;
mapping(uint => Human) list3;
// 3. User Defined Value Types
struct Human {
uint ID;
string name;
string surname;
uint age;
address address;
}
Human person1;
person1.ID = 2121212212121;
person1.name = "firstname";
person1.surname = "lastname";
person1.age = 25;
person1.address = 0x.....;
enum trafficLights {
RED,
YELLOW,
GREEN
}
|
-
Sabit Boyutlu Türler (Fixed Size Types)
bool
: Mantık değişkenidir. true
ve false
değerlerini tutar. Varsayılan olarak false
‘dur.
int
: Sayıları tuttuğumuz değişkendir. boyutu 256 byte’dır. int256
ile aynıdır, -2^256 to 2^256
uint
: Sayıları tuttuğumuz değişkendir. int
‘den farkı negatif sayıların dahil olmamasıdır. 0 to 2^256
address
: Cüzdan adreslerin tuttuğumuz değişkendir. Boyutu sabit 20 bytes’dır.
bytes32
: String değerleri hexadecimal olarak tutan değişkendir. 1-32 aralığında tanımlayabiliriz.
-
Dinamik Boyutlu Türler (Dynamic Size Types)
string
: String değerleri tuttuğumuz değişkendir.
bytes
: String değerleri hexadecimal olarak tutan değişkendir.
type[]
: Verileri liste olarak tuttuğumuz değişkendir. Örneğin: [1,2,3,4,5]
mapping(type => type)
: Verileri key,value olarak tutmamızı sağlayan değişkendir.
-
Kullanıcı Tanımlı Türler (User Defined Value Types)
Operatörler:
!
(mantıksal olumsuzlama)
&&
(mantıksal bağlaç, “ve”)
||
(mantıksal ayrılma, “veya”)
==
(eşitlik)
!=
(eşitsizlik)
Ether Birimleri
1 wei
= 1
1 gwei
= 10^9 wei = 1000000000
1 ether
= 10^18 wei = 1000000000000000000
Zaman Birimleri
1 seconds
= 1
1 minutes
= 60
1 hours
= 3600
1 days
= 86400
1 weeks
= 604800
Solidity’de 3 tür değişken vardır:
-
Yerel (Local)
- Blockchain’de saklanmaz.
- Fonksiyon içerisinde bildirilir.
-
Durum (State)
- Fonksiyon dışında bildirilir.
- Blockchain’de saklanır.
-
Global
- Blockchain ile ilgili değişkenler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Variables {
// State Variables
string public bestClub = "itu blockchain";
uint256 public founded = 2018;
bool private rich = false;
function showLocalVariables() public pure returns (uint) {
// Local Variables
uint number = 17;
return number;
}
function showGlobalVariablesBlockNumber() public view returns (uint) {
return block.number;
}
function showBlockVariablesMsgSender() public view returns (address) {
return msg.sender;
}
function add(int256 _number) public pure returns (int256) {
// Local Variables
int256 minus = -1;
int256 lucky = 33;
return _number + minus + lucky;
}
// Global Variables
// block.difficulty (uint) Current block difficulty
// block.gaslimit (uint) Current block gaslimit
// block.number (uint) Current block number
// block.timestamp (uint) Current block timestamp as seconds since unix epoch
// msg.data (bytes calldata) Complete calldata
// msg.sender (address payable)
}
|
Functions, Function Modifiers and Function Visibility
Function Modifiers, Fonksiyon Niteleyicileri
view
: Fonksiyonun blokchain’den veri okuyacağını fakat üzerinde değişiklik yapmayacağını bildirir.
1
2
3
4
5
6
7
8
9
10
11
|
contract View {
uint x = 7;
function show()
public
view
returns(uint)
{
return x;
}
}
|
pure
: Fonksiyonun blokchain’den hem veri okumayacağını hem de değişiklik yapmayacağını bildirir. State içerisinde herhangi bir şey okumayacak ve yazmayacak, sadece Contract içinde çalışacak bir işlev.
1
2
3
4
5
6
7
8
9
|
contract Pure {
function add(uint x, uint y)
public
pure
returns(uint)
{
return x + y;
}
}
|
Function Visibility, Fonksiyon Görünürlükleri
public
: Herhangi bir akıllı kontrat ve hesap çağırabilir.
private
: Yalnızca fonksiyonun tanımlı olduğu kontratın içerisinde çağırılabilir.
internal
: Tanımlı olduğu kontrat ile birlikte miras olarak alındığı kontrat tarafından da çağırılabili.,
external
: Yalnızca diğer akıllı kontratlar ve hesaplar tarafından çağıralabilir.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// Functions
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Functions {
// doğrudan erişebiliyorsun
uint public luckyNumber1 = 3;
uint luckyNumber = 7;
function showNumber() public view returns(uint) {
return luckyNumber;
}
function setNumber( uint _newNumber) public {
luckyNumber = _newNumber;
}
function nothing() public pure returns(uint, bool, bool) {
return (5, true, false);
}
function nothing2() public pure returns(uint x, bool y, bool z) {
// değişkenler tanımlandığı için return kullanmaya gerek yok
x = 5;
y = true;
z = false;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// Function Modifiers
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Functions {
//View
uint public number = 3;
function addToX(uint y) public view returns(uint){
return number + y;
}
function addAndView(uint a, uint b) public view returns (uint) {
return a * (b + 42) + block.timestamp;
}
//Pure
function addAndPure(uint a, uint b) public pure returns (uint) {
return a * (b + 42);
}
function hello() pure public returns(string memory) {
return 'Hello World!';
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
// Function Visibility
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Functions {
// Public, External, Internal, Private
// Public: Bu fonksiyonu hem dışarıdan kullanıcılar çağırabilir hemde kontrat çağırabilir
function add(uint a, uint b) public pure returns(uint){
return a + b;
}
function add2(uint c, uint d) public pure returns(uint){
return add(c,d);
}
function publicKeyword() public pure returns(string memory) {
return "Bu bir public fonksiyondur";
}
function callPublicKeyword() public pure returns(string memory) {
return publicKeyword();
}
// Private: Bu fonksiyona sadece bu kontrat ulaşabilir. Dışarıdan kimse bu fonksiyona ulaşamaz
function privateKeyword() private pure returns(string memory) {
return "Bu bir private fonskiyondur";
}
function callPrivateKeyword() public pure returns(string memory) {
return privateKeyword();
}
// Internal: sadece miras alan kontratlar bu fonksiyonu çağırabilir. Dışarıdan kullanıcı çağıramaz.
function internalKeyword() internal pure returns(string memory) {
return "bu bir internal fonskiyondur...";
}
function callInternalKeyword() public pure returns(string memory) {
return internalKeyword();
}
// External : Burada ise dışarıdan kullanıcalar çağırabilir fakat kontrat içerisnde çağrılmaz.
// function externalKeyword() external pure returns(string memory) {
// return "bu bir external fonskiyondur...";
// }
// function callExternalKeyword() public pure returns(string memory) {
// return externalKeyword();
// }
}
|
Constructor, Constant & Immutable
constructor
: Kontrat deploy edilirken yalnızca bir kere çalışan, daha sonrada bir daha çağırılamayan isteğe bağlı bir fonksiyondur.
1
2
3
4
5
6
7
|
contract Constructor {
uint x;
constructor(uint _x) {
x = _x;
}
}
|
constant
: Değeri değiştirilemeyen değişkenlerdir. Atanan değer kontrat deploy edildikten sonra bir daha değiştirilemez. Gaz maaliyetinden tasarruf sağlayabilir.
immutable
: Değeri değiştirilemeyen değişkenlerdir. Constant’tan farkı immutable ile işaretlenmiş değişkenin değerinin constructor ile başlangıçta değiştirilebiliyor oluşudur.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Constructor
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Constructor{
string public tokenName;
uint public totalSupply;
constructor(string memory name, uint number) {
tokenName = name;
totalSupply = number;
}
function set(uint number) public {
totalSupply = number;
}
}
|
1
2
3
4
5
6
7
8
9
10
|
// Constant
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Constants {
address public constant MY_ADDRESS = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc;
uint public constant MY_UINT = 123;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// Immutable
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Immutable {
address public immutable MY_ADDRESS;
uint public immutable MY_LUCKYNUMBER;
constructor(uint _myNumber) {
MY_ADDRESS = msg.sender;
MY_LUCKYNUMBER = _myNumber;
}
}
|
Control Structures
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// If Else Koşul Yapıları Kontrat Örneği
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract IfElse {
bytes32 private hashedPassord;
uint256 private loginCount;
constructor(string memory _password) {
hashedPassord = keccak256(abi.encode(_password));
}
function login(string memory _password) public returns (bool) {
if (hashedPassord == keccak256(abi.encode(_password))) {
loginCount++;
//loginCount += 1;
return true;
} else {
return false;
}
// return (hashedPassord == keccak256(abi.encode(_password)));
}
function loginlogin(string memory _password) public view returns (uint256) {
if (hashedPassord == keccak256(abi.encode(_password))) {
return 1;
} else {
return 0;
}
// return (hashedPassord == keccak256(abi.encode(_password)) ? 1 : 0);
}
function loginStatus() public view returns (uint256) {
if (loginCount == 0) {
return 0;
} else if (loginCount > 0 && loginCount != 1) {
return 1;
} else if (loginCount == 1) {
return 2;
} else {
return 3;
}
}
/*
&& -> ve
true && false -> false
|| -> veya
true || false -> true
>, <, >=, <=
*/
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
// For - While Döngüleri Kontrat Örneği
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Loops {
uint256[15] public numbers0;
uint256[15] public numbers1;
bool public state = true;
int256 public num = 0;
function listByFor() public {
uint256[15] memory nums = numbers0;
for (uint256 i = 0; i < nums.length; i++) {
//if(i == 13) continue;
//if(i == 14) break;
nums[i] = i;
}
numbers0 = nums;
}
function getArr0() public view returns (uint256[15] memory) {
return numbers0;
}
function listByWhile() public {
uint256 i = 0;
while (i < numbers1.length) {
numbers1[i] = i;
i++;
}
}
function getArr1() public view returns (uint256[15] memory) {
return numbers1;
}
function crashByWhile() public {
while (state) {
num++;
num--;
}
}
}
|
Mapping
Mapping, Solidity’e özel bir veri tipidir. Diğer dillerdeki Dictionary, Map, HashTable veri tiplerine benzetebiliriz.
Eğer doğrudan iterate etmemiz gereken durumlar yoksa veya array’de ardışık şekilde sıralamamız gerekmiyorsa Mapping kullanmalıyız. Mapping kullanmak büyük gas tasarrufları yapmamıza ve kolayca verilere erişmemize yarıyor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
// Mapping
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Mapping {
mapping(address => bool) public registered;
mapping(address => int256) public favNums;
function register(int256 _favNum) public {
//require(!registered[msg.sender], "Kullanıcınız daha önce kayıt yaptı.");
require(!isRegistered(), "Kullaniciniz daha once kayit yapti.");
registered[msg.sender] = true;
favNums[msg.sender] = _favNum;
}
function isRegistered() public view returns (bool) {
return registered[msg.sender];
}
function deleteRegistered() public {
require(isRegistered(), "Kullaniciniz kayitli degil.");
delete (registered[msg.sender]);
delete (favNums[msg.sender]);
}
}
contract NestedMapping {
mapping(address => mapping(address => uint256)) public debts;
function incDebt(address _borrower, uint256 _amount) public {
debts[msg.sender][_borrower] += _amount;
}
function decDebt(address _borrower, uint256 _amount) public {
require(debts[msg.sender][_borrower] >= _amount, "Not enough debt.");
debts[msg.sender][_borrower] -= _amount;
}
function getDebt(address _borrower) public view returns (uint256) {
return debts[msg.sender][_borrower];
}
}
|
Struct and Enum
Struct ve Enum veri tipleri.
memory
, is a temporary place to store data. memory
is used to make a transient reference to something in storage. Veriyi saklamak için kullanılan geçiçi bir yerdir. Sadece function scope içerisinde yer alır.
storage
, holds data between function calls. Fonksiyon bittikten sonra da contract içerisinde yaşamaya devam eder. Global değişkenleri tanımlarken kullanılır. Diğer programlama dillerindeki pointer kavramına benzetebiliriz.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
// Struct and Enum
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract StructEnum {
enum Status {
Taken, // 0
Preparing, // 1
Boxed, // 2
Shipped // 3
}
struct Order {
address customer;
string zipCode;
uint256[] products;
Status status;
}
Order[] public orders;
address public owner;
constructor() {
owner = msg.sender;
}
function createOrder(string memory _zipCode, uint256[] memory _products) external returns(uint256) {
require(_products.length > 0, "No products.");
Order memory order;
order.customer = msg.sender;
order.zipCode = _zipCode;
order.products = _products;
order.status = Status.Taken;
orders.push(order);
// orders.push(
// Order({
// customer: msg.sender,
// zipCode: _zipCode,
// products: _products,
// status: Status.Taken
// })
// );
// orders.push(Order(msg.sender, _zipCode, _products, Status.Taken));
return orders.length - 1; // 0 1 2 3
}
function advanceOrder(uint256 _orderId) external {
require(owner == msg.sender, "You are not authorized.");
require(_orderId < orders.length, "Not a valid order id.");
Order storage order = orders[_orderId];
require(order.status != Status.Shipped, "Order is already shipped.");
if (order.status == Status.Taken) {
order.status = Status.Preparing;
} else if (order.status == Status.Preparing) {
order.status = Status.Boxed;
} else if (order.status == Status.Boxed) {
order.status = Status.Shipped;
}
}
function getOrder(uint256 _orderId) external view returns (Order memory) {
require(_orderId < orders.length, "Not a valid order id.");
/*
Order memory order = orders[_orderId];
return order;
*/
return orders[_orderId];
}
function updateZip(uint256 _orderId, string memory _zip) external {
require(_orderId < orders.length, "Not a valid order id.");
Order storage order = orders[_orderId];
require(order.customer == msg.sender, "You are not the owner of the order.");
order.zipCode = _zip;
}
}
|
Modifiers
A modifier aims to change the behaviour of the function to which it is attached. Modifiers are code that can be run before and / or after a function call.
Modifiers can be used to:
- Restrict access
- Validate inputs
- Guard against reentrancy hack
Modifiers genelde contract sonuna yazılır (not necessary).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
// Modifiers
//SPDX-License-Identifier: Unlicensed
//@notice it's advanced version of previous parts example
pragma solidity ^0.8.0;
contract Modifier {
enum Status {
Taken,
Preparing,
Boxed,
Shipped
}
struct Order {
address customer;
string zipCode;
uint256[] products;
Status status;
}
Order[] public orders;
address public owner;
uint256 public txCount;
constructor() {
owner = msg.sender;
}
function createOrder(string memory _zipCode, uint256[] memory _products) checkProducts(_products) incTx external returns(uint256) {
// require(_products.length > 0, "No products.");
Order memory order;
order.customer = msg.sender;
order.zipCode = _zipCode;
order.products = _products;
order.status = Status.Taken;
orders.push(order);
return orders.length - 1;
}
function advanceOrder(uint256 _orderId) checkOrderId(_orderId) onlyOwner external {
// require(owner == msg.sender, "You are not authorized.");
// require(_orderId < orders.length, "Not a valid order id.");
Order storage order = orders[_orderId];
require(order.status != Status.Shipped, "Order is already shipped.");
if (order.status == Status.Taken) {
order.status = Status.Preparing;
} else if (order.status == Status.Preparing) {
order.status = Status.Boxed;
} else if (order.status == Status.Boxed) {
order.status = Status.Shipped;
}
}
function getOrder(uint256 _orderId) checkOrderId(_orderId) external view returns (Order memory) {
// require(_orderId < orders.length, "Not a valid order id.");
return orders[_orderId];
}
function updateZip(uint256 _orderId, string memory _zip) checkOrderId(_orderId) onlyCustomer(_orderId) incTx external {
// require(_orderId < orders.length, "Not a valid order id.");
Order storage order = orders[_orderId];
// require(order.customer == msg.sender, "You are not the owner of the order.");
order.zipCode = _zip;
}
modifier checkProducts(uint256[] memory _products) {
require(_products.length > 0, "No products.");
_;
}
modifier checkOrderId(uint256 _orderId) {
require(_orderId < orders.length, "Not a valid order id.");
_;
}
modifier incTx {
_;
txCount++;
}
modifier onlyOwner {
require(owner == msg.sender, "You are not authorized.");
_;
}
modifier onlyCustomer(uint256 _orderId) {
require(orders[_orderId].customer == msg.sender, "You are not the owner of the order.");
_;
}
}
|
Events
Standalone çalışan contract içerisinde önemli olmasa da DApp’ lerde önemli bir işlevi vardır. Akıllı sözleşmelerdeki işlemlerin sonuçlarını ve dışarı çıkarmak istedeğimiz bilgileri events yardımı ile yapabiliyoruz.
indexed
eğer yayınladığımız bir event’de bir değişkeni indexed olarak belirlersek sonrasında blockchain’den bu indexed değerlerini sorgulayabiliyoruz.
emit
event yayınlamak için kullanıyoruz.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
// Events
//SPDX-License-Identifier: Unlicensed
//@notice it's advanced version of previous parts example
pragma solidity ^0.8.0;
contract Events {
enum Status {
Taken,
Preparing,
Boxed,
Shipped
}
struct Order {
address customer;
string zipCode;
uint256[] products;
Status status;
}
Order[] public orders;
address public owner;
uint256 public txCount;
event OrderCreated(uint256 _orderId, address indexed _consumer);
event ZipChanged(uint256 _orderId, string _zipCode);
constructor() {
owner = msg.sender;
}
function createOrder(string memory _zipCode, uint256[] memory _products) checkProducts(_products) incTx external returns(uint256) {
Order memory order;
order.customer = msg.sender;
order.zipCode = _zipCode;
order.products = _products;
order.status = Status.Taken;
orders.push(order);
emit OrderCreated(orders.length - 1, msg.sender);
return orders.length - 1;
}
function advanceOrder(uint256 _orderId) checkOrderId(_orderId) onlyOwner external {
Order storage order = orders[_orderId];
require(order.status != Status.Shipped, "Order is already shipped.");
if (order.status == Status.Taken) {
order.status = Status.Preparing;
} else if (order.status == Status.Preparing) {
order.status = Status.Boxed;
} else if (order.status == Status.Boxed) {
order.status = Status.Shipped;
}
}
function getOrder(uint256 _orderId) checkOrderId(_orderId) external view returns (Order memory) {
return orders[_orderId];
}
function updateZip(uint256 _orderId, string memory _zip) checkOrderId(_orderId) onlyCustomer(_orderId) incTx external {
Order storage order = orders[_orderId];
order.zipCode = _zip;
emit ZipChanged(_orderId, _zip);
}
modifier checkProducts(uint256[] memory _products) {
require(_products.length > 0, "No products.");
_;
}
modifier checkOrderId(uint256 _orderId) {
require(_orderId < orders.length, "Not a valid order id.");
_;
}
modifier incTx {
_;
txCount++;
}
modifier onlyOwner {
require(owner == msg.sender, "You are not authorized.");
_;
}
modifier onlyCustomer(uint256 _orderId) {
require(orders[_orderId].customer == msg.sender, "You are not the owner of the order.");
_;
}
}
|
Sending Ethers
payable
: Ether transfer edebileceğimiz fonksiyon ve adresleri bildirir. payable
kullanmaz ve fonksiyon içerisinde msg.value
kullanmaya çalışırsak hata alırız.
Bir fonksiyona ether göndermek için fonksiyonun payable
olarak nitelenmiş olması gerekir
1
2
3
|
function fName() public payable {
/// @dev fonksiyonun işlevleri...
}
|
Bir adrese ether gönderebilmek için adresin payable
olarak nitelenmiş olması gerekir.
1
2
3
|
function fName() public {
payable(0x...).transfer(amount);
}
|
Ether göndermenin 3 farklı yolu:
transfer
: 2300 gas, throws error
send
: 2300 gas, bool döndürür
call
: gas ayarlanabilir, bool döndürür. İki tane değer döndürür (bool ve data)
Kontratların fonksiyonlar dışında ether alabilmesi için receive
ya da fallback
fonksiyonlarından en az birisinin tanımlanmış olması gerekir.
1
2
3
|
receive() external payable {}
fallback() external payable {}
|
- Eğer kontratta
receive
veya fallback
fonksiyonu yoksa kontrata ETH gönderemeyiz.
receive
ve fallback
fonksiyonlarının her ikisi de varsa
- Verinin olmadığı durumlarda (
msg.data
boş ise) receive
fonksiyonu çalışır
- Verinin olduğu durumlarda (
msg.data
dolu ise) fallback
fonksiyonu çalışır
- Sadece
fallback
fonksiyonu varsa her iki durumda da (msg.data
dolu/boş ise) fallback
fonksiyonu çalışır.
Hangi fonksiyon çağırılacak, fallback() or receive()?
1
2
3
4
5
6
|
flowchart TD
A[Send Ether] -->|ETH| B{Is msg.data empty?}
B -->|Yes| C{Is receive available?}
B -->|No| D[fallback]
C -->|Yes| E{receive}
C -->|No| F[fallback]
|
1
2
3
4
5
6
7
8
9
10
11
|
Ether gönder
|
msg.data boş mu?
/ \
evet hayır
/ \
receive() var mı? fallback()
/ \
evet hayır
/ \
receive() fallback()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
// Bank.sol
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Bank {
mapping(address => uint ) balances;
function sendEtherToContract() payable external {
balances[msg.sender] = msg.value;
}
function showBalance() external view returns(uint) {
return balances[msg.sender];
}
function withdraw(address payable to, uint amount) external {
// require(balances[msg.sender] >= amount,"yetersiz bakiye");
to.transfer(amount);
balances[to] += amount;
balances[msg.sender] -= amount;
}
function withdrawWithCall(address payable to, uint amount) external returns(bool) {
(bool sent, bytes memory data) = to.call{value: amount, gas: 424242}("data message");
balances[msg.sender] -= amount;
return sent;
}
// Transfer()
// Revert
// Send()
// true, false
// Call()
// bool, data
uint public receiveCount = 0;
uint public fallbackCount = 0;
receive() external payable {
receiveCount +=1;
}
fallback() external payable {
fallbackCount += 1;
}
}
|
Errors
error
revert
function will undo all state changes.
require
function should be used to check return values from calls to external contracts or to guarantee that valid conditions, such as inputs or contract state variables, are satisfied. Kullanıcı sebebi ile oluşacak hataları engellemek için kullanılır.
assert
function should only be used to examine invariants and test for internal problems.
Use require()
to:
- Validate user inputs ie.
require(input<20)
;
- Validate the response from an external contract ie.
require(external.send(amount))
;
- Validate state conditions prior to execution, ie.
require(block.number > SOME_BLOCK_NUMBER)
or require(balance[msg.sender]>=amount)
- Generally, you should use
require
most often
- Generally, it will be used towards the beginning of a function
Use revert()
to:
- Handle the same type of situations as
require()
, but with more complex logic.
Use assert()
to:
- Check for overflow/underflow, ie.
c = a+b; assert(c > b)
- Check invariants, ie.
assert(this.balance >= totalSupply);
- Validate state after making changes
- Prevent conditions which should never, ever be possible
- Generally, you will probably use
assert
less often
- Generally, it will be used towards the end of a function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
// Errors.sol
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Errors {
uint256 public totalBalance;
mapping(address => uint256) public userBalances;
error ExceedingAmount(address user, uint256 exceedingAmount);
error Deny(string reason);
receive() external payable {
revert Deny("No direct payments.");
}
fallback() external payable {
revert Deny("No direct payments.");
}
function pay() noZero(msg.value) external payable {
require(msg.value == 1 ether, "Only payments in 1 ether");
totalBalance += 1 ether; // 1e18
userBalances[msg.sender] += 1 ether; // 10000...0000
}
function withdraw(uint256 _amount) noZero(_amount) external {
uint256 initalBalance = totalBalance;
//require(userBalances[msg.sender] >= _amount, "Insufficient balance.");
if(userBalances[msg.sender] < _amount) {
//revert("Insufficient balance.");
revert ExceedingAmount(msg.sender, _amount - userBalances[msg.sender]);
}
totalBalance -= _amount;
userBalances[msg.sender] -= _amount;
// address => address payable
payable(msg.sender).transfer(_amount); // Doğrudan transfer methodunu çağrılırsa bir Re-Entrancy güvenlik açığı doğar. Bir fonksiyonu dışarıdan çağırmadan önce kendi kontratımızda yapılması gereken tüm değişikliklerin yapılması gerekiyor. Bu nedenle burada balance değişiklikleri yapılıyor.
assert(totalBalance < initalBalance);
}
modifier noZero(uint256 _amount) {
require(_amount != 0);
_;
}
}
|
Library
library
: Kontratlara benzer fakat ether alamaması ve durum değişkeni tutamaması ile farklılaşır. Kontratların içerisine gömülü fonksiyonların eklenmesine benzetilebilir.
using <libraryName> for <type>
tanımlaması ile de kullanılabilir.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
// Library.sol
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
library Math {
function plus(uint x, uint y) public pure returns(uint) {
return x + y;
}
function minus(uint x, uint y) public pure returns(uint) {
return x - y;
}
function multi(uint x, uint y) public pure returns(uint) {
return x * y;
}
function divide(uint x, uint y) public pure returns(uint) {
require(y != 0,"bu sayisi begenmedim!");
return x / y;
}
function min(uint x, uint y) public pure returns(uint) {
if( x <= y){
return x;
}else {
return y;
}
}
function max(uint x, uint y) public pure returns(uint) {
if( x >= y){
return x;
}else {
return y;
}
}
}
library Search {
function indexOf(uint[] memory list, uint data) public pure returns(uint) {
for (uint i = 0; i < list.length; i++) {
if (list[i] == data) {
return i;
}
}
return list.length;
}
}
contract Library {
using Math for uint;
using Search for uint[];
function trial1(uint[] memory x, uint y) public pure returns(uint) {
return x.indexOf(y); // Search.indexOf(x,y) Math.plus(x,y);
}
}
library Human {
struct Person {
uint age;
}
function birthday(Person storage _person) public {
_person.age += 1;
}
function showAge(Person storage _person) public view returns(uint) {
return _person.age;
}
}
contract HumanContract {
mapping(uint => Human.Person) people;
function newYear() public {
Human.birthday(people[0]);
}
function show() public view returns(uint) {
return Human.showAge(people[0]);
}
}
|
Data Locations
EVM (Ethereum Virtual Machine) ‘de 3 çeşit hafıza alanı (data location) bulunur.
storage
: Bu veriler blokzincirde tutulur.
memory
: Bu veriler fonksiyon çağrıldıktan itibaren EVM tarafından ayrılan özel bir bölgede tutulur ve fonksiyon bittiğinde silinir.
calldata
: Bu veriler fonksiyon çağrılırken, çağrının (transaction) içerisinde tutulur (msg.data
). Bu veriler sadece okunabilir (read-only).
bytes
, string
, uint256[]
, struct
gibi referans tipleri fonksiyonlarda
kullanılırken bu verilerin hangi hafıza alanından alınacağı belirtilmelidir.
calldata
sadece fonksiyon parametreleri için kullanılabilir. Eğer fonksiyon içerisinde verilen değerleri doğrudan okumak isterseniz bunu kullanın.
memory
yi fonksiyon içerisinde eğer bir değişiklik yapmayacaksanız, sadece okuma yapacaksaksanız kullanın.
storage
sadece fonksiyon gövdesinde kullanılabilir. Eğer fonksiyon içerisinde bir değişiklik yapacaksaksanız bunu kullanın.
Bunu yapmanız hem daha okunabilir, anlaşılabilir kodlar yazmanızı hem de gas tasarrufu yapmanızı sağlar. storage
üzerinde yapılan işlemler daha pahalı iken memory
üzerinde yapılan daha ucuzdur, calldata
daha da ucuzdur.
1
2
3
|
function(string memory/calldata parameterString) external {
string memory/storage bodyString = "";
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
// DataLocations.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
Kontrat <---- Kontrata yapılan çağrı
------- -------------
Kontrat depolama alanı Fonksiyon için ayrılan hafıza ve çağrıdaki data alanı
memory: Geçici hafıza
storage: Kalıcı hafıza
calldata: Çağrıdaki argümanlar
bytes, string, array, struct
* Değer tipleri (uint, int, bool, bytes32) kontrat üzerinde storage,
fonksiyon içinde memory'dir
* mapping'ler her zaman kontrat üzerinde tanımlanır ve storage'dadır.
*/
struct Student {
uint8 age;
uint16 score;
string name;
}
contract School {
uint256 totalStudents = 0; // storage
mapping(uint256 => Student) students; // storage
function addStudent(string calldata name, uint8 age, uint16 score) external {
uint256 currentId = totalStudents++;
students[currentId] = Student(age, score, name);
}
function changeStudentInfoStorage(
uint256 id, // memory
string calldata newName, // calldata
uint8 newAge, // memory
uint16 newScore // memory
) external {
Student storage currentStudent = students[id];
currentStudent.name = newName;
currentStudent.age = newAge;
currentStudent.score = newScore;
}
/**
@dev Bu işe yaramayacaktır, çünkü oluşturulan currentStudent ömrü
fonksiyonun bitişine kadar olan bir değişken ve fonksiyon tamamlandığında
silinecektir
*/
function changeStudentInfoMemory(
uint256 id, // memory
string calldata newName, // calldata
uint8 newAge, // memory
uint16 newScore // memory
) external {
Student memory currentStudent = students[id];
currentStudent.name = newName;
currentStudent.age = newAge;
currentStudent.score = newScore;
}
function getStudentName(uint256 id) external view returns(string memory) {
return students[id].name;
}
}
|
Inheritance
is
: Solidity supports both single as well as multiple inheritance. Solidity çoklu kalıtımı destekleyen bir programlama dilidir. Kontratlar is
anahtar sözcüğü ile diğer kontratları miras olarak alabilir.
virtual
: Bir alt kontrat tarafından fonksiyonun geçersiz kılınabileceğini bildiren niteleyicidir. (Bir kontratı miras olarak aldığımızda virtual
işaretli fonksiyonu tekrar düzenleyebilir ve içeriğini değiştirebiliriz.)
override
: Bir üst kontratta bulunan virtual
ile işaretlenmiş fonksiyonları geçersiz kılmamızı ve tekrardan tanımladığımızı bildiren niteleyicidir. (Miras aldığımız kontrakt içerisindeki özelliğini değiştirmek istediğimiz fonksiyon override
olarak işaretlenmeli.)
super
: Miras sırası önemlidir. C3-linearization kurallarına göre super
anahtar sözcüğü ile miras alınan kontrata erişebiliriz.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// Inheritance.sol
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract A {
uint public x;
uint public y;
function setX(uint _x) virtual public {
x = _x;
}
function setY(uint _y) virtual public {
y = _y;
}
}
// Derived Contract
contract B is A {
uint public z;
function setZ(uint _z) public {
z = _z;
}
function setX(uint _x) override public {
x = _x + 2;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// SuperHuman.sol
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Human {
function sayHello() public pure virtual returns(string memory) {
return "itublockchain.com adresi uzerinden kulube uye olabilirsiniz :)";
}
}
contract SuperHuman is Human {
function sayHello() public pure override returns(string memory) {
return "Selamlar itu blockchain uyesi, nasilsin ? :)";
}
function welcomeMsg(bool isMember) public pure returns(string memory) {
return isMember ? sayHello() : super.sayHello();
}
}
|
import
: to import local and external files. You can also import from GitHub by simply copying the url.
Example below imports Openzeppelin Ownable Contract.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Import.sol
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
// import "@openzeppelin/contracts/access/Ownable.sol"; // Method1: import from npm package
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol"; // Method2: import from github repository
contract Wallet is Ownable {
fallback() payable external {}
function sendEther(address payable to, uint amount) onlyOwner public {
to.transfer(amount);
}
function showBalance() public view returns(uint) {
return address(this).balance;
}
}
|
Interacting with Other Contracts/Addresses
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Interact {
address public caller;
mapping(address => uint256) public counts;
function callThis() external {
caller = msg.sender;
counts[msg.sender]++;
}
}
contract Pay {
mapping(address => uint256) public userBalances;
// receive, fallback
function payEth(address _payer) external payable {
userBalances[_payer] += msg.value;
}
}
// msg.sender -> A (mesaj yollayan: msg.sender) -> B (mesaj yollayan: A adresi)
contract Caller {
Interact interact;
constructor(address _interactContract) {
interact = Interact(_interactContract);
}
function callInteract() external {
interact.callThis();
}
function readCaller() external view returns (address) {
return interact.caller();
}
function readCallerCount() public view returns (uint256) {
return interact.counts(msg.sender);
}
function payToPay(address _payAddress) public payable {
Pay pay = Pay(_payAddress);
pay.payEth{value: msg.value}(msg.sender);
// Pay(_payAddress).payEth{value: msg.value}(msg.sender);
}
function sendEthByTransfer() public payable {
payable(address(interact)).transfer(msg.value);
}
}
|
Interfaces
Interface’ler (Arayüzler) çalışma mantığı farklı olan ama yaptığı iş aynı olan kontratların (örneğin token kontratları) ortak bir standarda sahip olmasını böylece bu kontratlarla çalışmak isteyen birinin her bir kontrata özgü kod yazmak yerine bu standarda uygun tek bir kod yazmasını sağlar.
ERC20, ERC721, ERC1155 gibi standartlar aslında bir interface şeklinde tanımlanmıştır.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
// Interfaces.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
Interfaces (Arayüz):
OOP'deki Polymorphism konseptinin uygulanma biçimlerinden biridir.
Parent a
Child1 ac, Child2 ab, Child3 ad
Araç
Kamyon, Otomobil, Karavan
fn tekerlekSayısıAl (Araç a) -> int tekerlekSayısı:
return a.tekerlekSayısı;
Sınıflar arasında etkileşim için bir standard oluşturur.
Solidity'de sınıflar -> kontratlar
*/
contract EthSender {
function _send(address payable to, uint256 amount) private {
to.transfer(amount);
}
function sendWithStrategy(address strategyAddress) external {
(address payable to, uint256 amount) = IStrategy(strategyAddress).getAddressAndAmount();
_send(to, amount);
}
receive() external payable { }
}
abstract contract Strategy {
// Virtual: Benden sonra gelen bu fonksiyonu değiştirebilir
function getAddressAndAmount() external virtual view returns(address payable, uint256);
}
interface IStrategy {
function getAddressAndAmount() external view returns(address payable, uint256);
}
contract AddressStrategy1 {
uint256 constant ETHER_AMOUNT = 0.1 ether;
function getAddressAndAmount() external pure returns(address payable, uint256) {
return(payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2), ETHER_AMOUNT);
}
}
contract AddressStrategy2 {
uint256 constant ETHER_AMOUNT = 0.1 ether;
function getAddressAndAmountV2() external pure returns(address payable, uint256) {
uint256 amount = ETHER_AMOUNT * 5;
return(payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2), amount);
}
}
contract AddressStrategy3 is Strategy {
uint256 constant ETHER_AMOUNT = 0.1 ether;
// Override: Benden önce gelen fonksiyonu değiştiriyorum
function getAddressAndAmount() external pure override returns(address payable, uint256) {
uint256 amount = ETHER_AMOUNT * 5;
return(payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2), amount);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// Vault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IToken.sol";
/// @title Vault
/// @notice Allows users to deposit and withdraw any token (?)
contract Vault {
/// @dev User -> Token -> Balance
mapping(address => mapping(IToken => uint256)) tokenBalances;
function depositToken(IToken token, uint256 amount) external {
token.transferFrom(msg.sender, address(this), amount);
tokenBalances[msg.sender][token] += amount;
}
function withdrawToken(IToken token, uint256 amount) external {
tokenBalances[msg.sender][token] -= amount;
token.transfer(msg.sender, amount);
}
function getBalanceOf(address user, IToken token) external view returns(uint256) {
return token.balanceOf(user);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// IToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IToken {
// Fonksiyonlarda override gerekli
function transferFrom(address from, address to, uint256 amount) external;
function transfer(address to, uint256 amount) external;
function balanceOf(address user) external view returns(uint256);
// Eventlerde override gerekli değil
event Transfer(address indexed from, address indexed to, uint256 amount);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// TokenA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IToken.sol";
contract TokenA is IToken {
uint256 constant private OWNER_BALANCE = 1000 * 10**18;
mapping(address => uint256) balances;
// Allower -> Allowee -> Amount
mapping(address => mapping(address => uint256)) allowances;
constructor() {
balances[msg.sender] = OWNER_BALANCE;
}
function transfer(address recipient, uint256 amount) external override {
require(recipient != address(0), "XFER_TO_ZERO");
require(balances[msg.sender] >= amount, "INSUFFICENT_BAL");
balances[msg.sender] -= amount;
balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
}
function transferFrom(address from, address to, uint256 amount) external override {
require(to != address(0), "XFER_TO_ZERO");
require(allowances[from][msg.sender] >= amount, "INSUFFICIENT_ALLOWANCE");
require(balances[from] >= amount, "INSUFFICENT_BALANCE");
allowances[from][msg.sender] -= amount;
balances[from] -= amount;
balances[to] += amount;
}
function increaseAllowance(address recipient, uint256 amount) external {
require(recipient != address(0), "APPR_TO_ZERO");
allowances[msg.sender][recipient] += amount;
}
function decreaseAllowance(address recipient, uint256 amount) external {
require(recipient != address(0), "APPR_TO_ZERO");
allowances[msg.sender][recipient] -= amount;
}
function balanceOf(address user) external view override returns(uint256) {
return balances[user];
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// TokenB.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IToken.sol";
contract TokenB is IToken{
uint256 constant private OWNER_BALANCE = 1000 * 10**18;
mapping(address => uint256) balances;
// Allower -> Allowee -> Amount
mapping(address => mapping(address => uint256)) allowances;
constructor() {
balances[msg.sender] = OWNER_BALANCE;
}
function transfer(address recipient, uint256 amount) external override {
require(recipient != address(0), "XFER_TO_ZERO");
require(balances[msg.sender] >= amount, "INSUFFICENT_BAL");
balances[msg.sender] -= amount;
balances[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
}
function transferFrom(address from, address to, uint256 amount) external override {
require(to != address(0), "XFER_TO_ZERO");
require(allowances[from][msg.sender] >= amount, "INSUFFICIENT_ALLOWANCE");
require(balances[from] >= amount, "INSUFFICENT_BALANCE");
allowances[from][msg.sender] -= amount;
balances[from] -= amount;
balances[to] += amount;
}
function increaseAllowance(address recipient, uint256 amount) external {
require(recipient != address(0), "APPR_TO_ZERO");
allowances[msg.sender][recipient] += amount;
}
function decreaseAllowance(address recipient, uint256 amount) external {
require(recipient != address(0), "APPR_TO_ZERO");
allowances[msg.sender][recipient] -= amount;
}
function balanceOf(address user) external view override returns(uint256) {
return balances[user];
}
}
|
Call
call
methodu aracılığıyla low-level çağrılar yapılıyor. Diğer akıllı kontratlar ile etkileşmemize yarar. Diğer akıllı kontratların fallback
methodlarını tetiklemek veya doğrudan sadece bir ETH değeri göndermek için kullanılması öneriliyor. call
methodu aracılığı ile diğer akıllı kontratlardaki fonksiyonu, eğer adını ve parametrelerini doğru şekilde biliyorsak çağırabiliyoruz. Bu da kontratın ABI
bilgisine ve kontratın kendisine ihtiyacımız olmadan akıllı kontratları çağırmaya yarıyor.
call
methoduna parametreleri doğrudan giremeyiz. abi.encodeWithSignature()
methodu yardımı ile hash’leyerek girebiliyoruz. abi.encodeWithSignature()
methodunun ilk parametresi çalıştırmak istediğimiz fonksiyon ve fonksiyona ait parametrelerin tipleri, diğer parametreler ise fonksiyona gönderilecek değerlerdir. Eğer ilk parametre olan fonksiyonu bilmiyorsak veya yanlış fonksiyon çağırırsak, bu fallback
methodunu tetikler. Bu doğru bir kullanım değildir.
1
|
_contract.call(abi.encodeWithSignature("inc(uint256, string)", _amount, _incrementer));
|
call
methodu iki değer döndürüyor. Değerlerden birisi bool
, diğeri bytes
. İkinci değeri doğrudan değil hash’leyerek döndürüyor. abi.decode()
ile bu değere erişebiliyoruz.
1
|
uint256 _total = abi.decode(res, (uint256));
|
call
methodu ile bir kontratın fallback
methodunu tetiklemek için süslü parantez kullanarak msg.value
değerini veya herhangi bir değer (1 ether vs.) gönderebiliyoruz. Eğer bu boş bir çağrıysa, yani herhangi bir fonksiyonu tetiklemek istemiyorsak parantez içerisine boş tırnak işareti koymamız gerekiyor.
call
methodunun döndürdüğü ikinci değişkeni kullanmak istemiyorsak, ilk değerden sonra virgül yazıp gerisini boş bırakıyoruz.
1
|
(bool err,) = _contract.call{value: msg.value}("");
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
// Call.sol
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract Test {
uint256 public total = 0;
uint256 public fallbackCalled = 0;
string public incrementer;
fallback() external payable {
fallbackCalled += 1;
}
function inc(uint256 _amount, string memory _incrementer) external returns(uint256) {
total += _amount;
incrementer = _incrementer;
return total;
}
}
contract Caller {
function testCall(address _contract, uint256 _amount, string memory _incrementer) external returns (bool, uint256) {
(bool err, bytes memory res) = _contract.call(abi.encodeWithSignature("inc(uint256, string)", _amount, _incrementer));
uint256 _total = abi.decode(res, (uint256));
return (err, _total);
}
function payToFallback(address _contract) external payable {
(bool err,) = _contract.call{value: msg.value}("");
if(!err) revert();
}
}
|
Creating Contracts
Akıllı kontratlar aracılığıyla farklı akıllı kontratlar deploy edilmesi. Bunlara Factory Contract
adı veriliyor.
Yeni bir kontrat deploy etme işlemi başka bir akıllı kontrat ile yapılsa bile çok gas harcayan maliyetli bir iştir. Ne kadar maliyetli olduğu kontratın ne kadar karmaşık olduğu ile ilgili. O yüzden bu tarz örnekleri mümkün olduğunca kullanmaktan kaçınmalıyız.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
// Factory Contract
//SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;
contract VaultFactory {
mapping(address => Vault[]) public userVaults;
function createVault() external {
Vault vault = new Vault(msg.sender);
userVaults[msg.sender].push(vault);
}
function createVaultWithPayment() external payable {
Vault vault = (new Vault){value: msg.value}(msg.sender);
userVaults[msg.sender].push(vault);
}
}
contract Vault {
address public owner;
uint256 private balance;
constructor(address _owner) payable {
owner = _owner;
balance += msg.value;
}
fallback() external payable {
balance += msg.value;
}
receive() external payable {
balance += msg.value;
}
function getBalance() external view returns (uint256) {
return balance;
}
function deposit() external payable {
balance += msg.value;
}
function withdraw(uint256 _amount) external {
require(msg.sender == owner, "You are not authorized.");
balance -= _amount;
payable(owner).transfer(_amount);
}
}
|