Solidity - Hướng dẫn nhanh

Solidity là một ngôn ngữ lập trình cấp cao, định hướng hợp đồng để thực hiện các hợp đồng thông minh. Solidity bị ảnh hưởng nhiều bởi C ++, Python và JavaScript và đã được thiết kế để nhắm mục tiêu Máy ảo Ethereum (EVM).

Solidity được định kiểu tĩnh, hỗ trợ kế thừa, thư viện và ngôn ngữ lập trình kiểu phức tạp do người dùng định nghĩa.

Bạn có thể sử dụng Solidity để tạo hợp đồng cho các mục đích sử dụng như bỏ phiếu, huy động vốn từ cộng đồng, đấu giá mù và ví nhiều chữ ký.

Ethereum là gì?

Ethereum là một ví dụ phi tập trung. nền tảng blockchain chạy các hợp đồng thông minh tức là các ứng dụng chạy chính xác như được lập trình mà không có bất kỳ khả năng nào về thời gian chết, kiểm duyệt, gian lận hoặc sự can thiệp của bên thứ ba.

Máy ảo Ethereum (EVM)

Máy ảo Ethereum, còn được gọi là EVM, là môi trường thời gian chạy cho các hợp đồng thông minh trong Ethereum. Máy ảo Ethereum tập trung vào việc cung cấp bảo mật và thực thi mã không đáng tin cậy bởi các máy tính trên toàn thế giới.

EVM chuyên ngăn chặn các cuộc tấn công Từ chối dịch vụ và đảm bảo rằng các chương trình không có quyền truy cập vào trạng thái của nhau, đảm bảo giao tiếp có thể được thiết lập mà không có bất kỳ sự can thiệp tiềm ẩn nào.

Máy ảo Ethereum đã được thiết kế để phục vụ như một môi trường thời gian chạy cho các hợp đồng thông minh dựa trên Ethereum.

Hợp đồng thông minh là gì?

Hợp đồng thông minh là một giao thức máy tính nhằm tạo điều kiện kỹ thuật số, xác minh hoặc thực thi việc đàm phán hoặc thực hiện hợp đồng. Hợp đồng thông minh cho phép thực hiện các giao dịch đáng tin cậy mà không cần bên thứ ba. Các giao dịch này có thể theo dõi và không thể đảo ngược.

Khái niệm hợp đồng thông minh lần đầu tiên được đề xuất bởi Nick Szabo vào năm 1994. Szabo là một học giả pháp lý và nhà mật mã học nổi tiếng với việc đặt nền móng cho tiền kỹ thuật số.

Sẽ không sao nếu bạn chưa hiểu về Hợp đồng thông minh ngay bây giờ, chúng ta sẽ đi vào chi tiết hơn ở phần sau.

Chương này giải thích cách chúng ta có thể thiết lập trình biên dịch Solidity trên máy CentOS. Nếu bạn không có máy Linux thì bạn có thể sử dụng Trình biên dịch Trực tuyến của chúng tôi cho các hợp đồng nhỏ và để học nhanh Solidity.

Phương pháp 1 - npm / Node.js

Đây là cách nhanh nhất để cài đặt trình biên dịch Solidity trên Máy CentoS của bạn. Chúng tôi thực hiện các bước sau để cài đặt Solidity Compiler -

Cài đặt Node.js

Trước tiên, hãy đảm bảo rằng bạn có sẵn node.js trên máy CentOS của mình. Nếu nó không có sẵn thì hãy cài đặt nó bằng các lệnh sau:

# First install epel-release
$sudo yum install epel-release

# Now install nodejs
$sudo yum install nodejs

# Next install npm (Nodejs Package Manager )
$sudo yum install npm

# Finally verify installation
$npm --version

Nếu mọi thứ đã được cài đặt thì bạn sẽ thấy một kết quả như thế này -

3.10.10

Cài đặt solc

Khi bạn đã cài đặt trình quản lý gói Node.js thì bạn có thể tiến hành cài đặt trình biên dịch Solidity như bên dưới:

$sudonpm install -g solc

Lệnh trên sẽ cài đặt chương trình solcjs và sẽ cung cấp nó trên toàn cầu thông qua hệ thống. Bây giờ bạn có thể kiểm tra trình biên dịch Solidity của mình bằng cách đưa ra lệnh sau:

$solcjs-version

Nếu mọi thứ suôn sẻ, thì điều này sẽ in ra một thứ như sau:

0.5.2+commit.1df8f40c.Emscripten.clang

Bây giờ bạn đã sẵn sàng sử dụng solcjs có ít tính năng hơn trình biên dịch Solidity tiêu chuẩn nhưng nó sẽ cung cấp cho bạn một điểm khởi đầu tốt.

Phương pháp 2 - Hình ảnh Docker

Bạn có thể kéo một hình ảnh Docker và bắt đầu sử dụng nó để bắt đầu với lập trình Solidity. Sau đây là các bước đơn giản. Sau đây là lệnh để kéo một Hình ảnh Solidity Docker.

$docker pull ethereum/solc:stable

Sau khi hình ảnh docker được tải xuống, chúng tôi có thể xác minh nó bằng lệnh sau.

$docker run ethereum/solc:stable-version

Điều này sẽ in một cái gì đó như sau:

$ docker run ethereum/solc:stable -version

solc, the solidity compiler commandlineinterfaceVersion: 0.5.2+commit.1df8f40c.Linux.g++

Phương pháp 3: Cài đặt gói nhị phân

Nếu bạn sẵn sàng cài đặt trình biên dịch chính thức đầy đủ trên máy Linux của mình, hãy kiểm tra trang web chính thức Cài đặt trình biên dịch Solidity.

Tệp nguồn Solidity có thể chứa bất kỳ số lượng định nghĩa hợp đồng, chỉ thị nhập và chỉ thị pragma nào.

Hãy bắt đầu với một tệp nguồn đơn giản của Solidity. Sau đây là một ví dụ về tệp Solidity:

pragma solidity >=0.4.0 <0.6.0;
contract SimpleStorage {
   uint storedData;
   function set(uint x) public {
      storedData = x;
   }
   function get() public view returns (uint) {
      return storedData;
   }
}

Pragma

Dòng đầu tiên là một chỉ thị pragma cho biết rằng mã nguồn được viết cho phiên bản Solidity 0.4.0 hoặc bất kỳ thứ gì mới hơn không phá vỡ chức năng, nhưng không bao gồm phiên bản 0.6.0.

Chỉ thị pragma luôn là cục bộ cho tệp nguồn và nếu bạn nhập tệp khác, pragma từ tệp đó sẽ không tự động áp dụng cho tệp nhập.

Vì vậy, một pragma cho một tệp sẽ không được biên dịch sớm hơn phiên bản 0.4.0 và nó cũng sẽ không hoạt động trên trình biên dịch bắt đầu từ phiên bản 0.5.0 sẽ được viết như sau:

pragma solidity ^0.4.0;

Ở đây, điều kiện thứ hai được thêm vào bằng cách sử dụng ^.

Hợp đồng

Hợp đồng Solidity là một tập hợp mã (các chức năng của nó) và dữ liệu (trạng thái của nó) nằm tại một địa chỉ cụ thể trên Ethereumblockchain.

Dòng uintstoredData khai báo một biến trạng thái được gọi là ManagedData kiểu uint và các hàm set và get có thể được sử dụng để sửa đổi hoặc truy xuất giá trị của biến.

Nhập tệp

Mặc dù ví dụ trên không có câu lệnh nhập nhưng Solidity hỗ trợ các câu lệnh nhập rất giống với các câu lệnh có sẵn trong JavaScript.

Câu lệnh sau nhập tất cả các ký hiệu chung từ "tên tệp".

import "filename";

Ví dụ sau đây tạo một biểu tượng toàn cầu mới SymbolName có các thành viên là tất cả các biểu tượng chung từ "tên tệp".

import * as symbolName from "filename";

Để nhập tệp x từ cùng thư mục với tệp hiện tại, hãy sử dụng nhập "./x" dưới dạng x ;. Nếu bạn sử dụng nhập "x" là x; thay vào đó, một tệp khác có thể được tham chiếu trong "thư mục bao gồm" toàn cầu.

Từ khóa dành riêng

Sau đây là các từ khóa dành riêng trong Solidity -

trừu tượng sau bí danh ứng dụng
Tự động trường hợp nắm lấy bản sao của
mặc định định nghĩa sau cùng bất biến
dụng cụ trong nội tuyến để cho
vĩ mô trận đấu có thể thay đổi vô giá trị
của ghi đè một phần lời hứa
tài liệu tham khảo có thể di dời niêm phong kích thước
tĩnh ủng hộ công tắc điện thử
typedef loại không được kiểm tra

Chúng tôi đang sử dụng Remix IDE để biên dịch và chạy cơ sở mã Solidity của chúng tôi.

Step 1 - Sao chép mã đã cho trong Phần mã Remix IDE.

Thí dụ

pragma solidity ^0.5.0;
contract SolidityTest {
   constructor() public{
   }
   function getResult() public view returns(uint){
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Step 2 - Trong Tab Biên dịch, bấm Start to Compile cái nút.

Step 3 - Trong Tab Run, nhấp vào Deploy cái nút.

Step 4 - Trong Tab Run, Chọn SolidityTest at 0x... trong trình đơn thả xuống.

Step 5 - Bấm getResult Nút để hiển thị kết quả.

Đầu ra

0: uint256: 3

Solidity hỗ trợ cả C-style và C ++ - style comment, Do đó -

  • Bất kỳ văn bản nào giữa // và cuối dòng đều được coi là một chú thích và bị Solidity Compiler bỏ qua.

  • Bất kỳ văn bản nào giữa các ký tự / * và * / đều được coi là một nhận xét. Điều này có thể kéo dài nhiều dòng.

Thí dụ

Ví dụ sau đây cho thấy cách sử dụng chú thích trong Solidity.

function getResult() public view returns(uint){
   // This is a comment. It is similar to comments in C++

   /*
      * This is a multi-line comment in solidity
      * It is very similar to comments in C Programming
   */
   uint a = 1;
   uint b = 2;
   uint result = a + b;
   return result;
}

Trong khi viết chương trình bằng bất kỳ ngôn ngữ nào, bạn cần sử dụng các biến khác nhau để lưu trữ các thông tin khác nhau. Các biến không là gì ngoài các vị trí bộ nhớ dành riêng để lưu trữ các giá trị. Điều này có nghĩa là khi bạn tạo một biến, bạn dành một số không gian trong bộ nhớ.

Bạn có thể muốn lưu trữ thông tin của nhiều kiểu dữ liệu khác nhau như ký tự, ký tự rộng, số nguyên, dấu phẩy động, dấu chấm động kép, boolean, v.v. Dựa trên kiểu dữ liệu của một biến, hệ điều hành phân bổ bộ nhớ và quyết định những gì có thể được lưu trữ trong bộ nhớ dành riêng.

Các loại giá trị

Solidity cung cấp cho lập trình viên một loạt các kiểu dữ liệu tích hợp sẵn cũng như do người dùng xác định. Bảng sau liệt kê bảy kiểu dữ liệu C ++ cơ bản:

Kiểu Từ khóa Giá trị
Boolean bool đúng sai
Số nguyên int / uint Các số nguyên có dấu và không dấu có kích thước khác nhau.
Số nguyên int8 đến int256 Đã ký int từ 8 bit đến 256 bit. int256 giống với int.
Số nguyên uint8 đến uint256 Int không dấu từ 8 bit đến 256 bit. uint256 cũng giống như uint.
Số điểm cố định cố định / không cố định Số điểm cố định có dấu và không dấu có kích thước khác nhau.
Số điểm cố định cố định / không cố định Số điểm cố định có dấu và không dấu có kích thước khác nhau.
Số điểm cố định fixedMxN Số điểm cố định có dấu trong đó M đại diện cho số bit được lấy theo loại và N đại diện cho các điểm thập phân. M nên chia hết cho 8 và đi từ 8 đến 256. N có thể từ 0 đến 80. cố định giống với cố định.128x18.
Số điểm cố định ufixedMxN Số điểm cố định không dấu trong đó M đại diện cho số bit được lấy theo loại và N đại diện cho các điểm thập phân. M nên chia hết cho 8 và đi từ 8 đến 256. N có thể từ 0 đến 80. ufixed giống như ufixed128x18.

Địa chỉ

địa chỉ giữ giá trị 20 byte đại diện cho kích thước của địa chỉ Ethereum. Một địa chỉ có thể được sử dụng để lấy số dư bằng phương pháp .balance và có thể được sử dụng để chuyển số dư sang một địa chỉ khác bằng phương thức .transfer.

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

Solidity hỗ trợ ba loại biến.

  • State Variables - Các biến có giá trị được lưu trữ vĩnh viễn trong bộ lưu trữ hợp đồng.

  • Local Variables - Các biến có giá trị hiện diện cho đến khi hàm đang thực thi.

  • Global Variables - Các biến đặc biệt tồn tại trong không gian tên toàn cầu được sử dụng để lấy thông tin về blockchain.

Solidity là một ngôn ngữ được định kiểu tĩnh, có nghĩa là trạng thái hoặc kiểu biến cục bộ cần được chỉ định trong khi khai báo. Mỗi biến được khai báo luôn có một giá trị mặc định dựa trên kiểu của nó. Không có khái niệm "không xác định" hoặc "null".

Biến số đưa ra

Các biến có giá trị được lưu trữ vĩnh viễn trong bộ lưu trữ hợp đồng.

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData;      // State variable
   constructor() public {
      storedData = 10;   // Using State variable
   }
}

Biến cục bộ

Các biến có giá trị chỉ có sẵn trong một hàm mà nó được xác định. Các tham số của hàm luôn là cục bộ của hàm đó.

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result; //access the local variable
   }
}

Thí dụ

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: uint256: 10

Biến toàn cục

Đây là những biến đặc biệt tồn tại trong không gian làm việc toàn cầu và cung cấp thông tin về blockchain và các thuộc tính giao dịch.

Tên Lợi nhuận
blockhash (uint blockNumber) trả về (byte32) Băm của khối đã cho - chỉ hoạt động cho 256 khối gần đây nhất, không bao gồm khối hiện tại,
block.coinbase (địa chỉ có thể thanh toán) Địa chỉ của người khai thác khối hiện tại
block.difficulty (uint) Khó khăn khối hiện tại
block.gaslimit (uint) Gaslimit khối hiện tại
block.number (uint) Số khối hiện tại
block.timestamp (uint) Dấu thời gian khối hiện tại dưới dạng giây kể từ kỷ nguyên unix
gasleft () trả về (uint256) Khí còn lại
msg.data (byte calldata) Calldata hoàn chỉnh
msg.sender (địa chỉ có thể thanh toán) Người gửi tin nhắn (người gọi hiện tại)
msg.sig (byte4) Bốn byte đầu tiên của calldata (mã định danh hàm)
msg.value (uint) Số wei đã gửi cùng với tin nhắn
bây giờ (uint) Dấu thời gian khối hiện tại
tx.gasprice (gợi ý) Giá gas giao dịch
tx.origin (địa chỉ thanh toán) Người gửi giao dịch

Tên biến thể rắn

Trong khi đặt tên cho các biến của bạn trong Solidity, hãy ghi nhớ các quy tắc sau.

  • Bạn không nên sử dụng bất kỳ từ khóa nào dành riêng cho Solidity làm tên biến. Những từ khóa này sẽ được đề cập trong phần tiếp theo. Ví dụ, tên biến break hoặc boolean không hợp lệ.

  • Tên biến solidity không được bắt đầu bằng chữ số (0-9). Chúng phải bắt đầu bằng một chữ cái hoặc một ký tự gạch dưới. Ví dụ: 123test là một tên biến không hợp lệ nhưng _123test là một biến hợp lệ.

  • Tên biến solidity có phân biệt chữ hoa chữ thường. Ví dụ: Tên và tên là hai biến khác nhau.

Phạm vi của các biến cục bộ được giới hạn trong chức năng mà chúng được định nghĩa nhưng các biến trạng thái có thể có ba loại phạm vi.

  • Public- Các biến trạng thái công khai có thể được truy cập nội bộ cũng như thông qua tin nhắn. Đối với biến trạng thái công khai, một hàm getter tự động được tạo.

  • Internal - Các biến trạng thái nội bộ chỉ có thể được truy cập nội bộ từ hợp đồng hiện tại hoặc hợp đồng bắt nguồn từ nó mà không cần sử dụng nó.

  • Private - Các biến trạng thái riêng chỉ có thể được truy cập nội bộ từ hợp đồng hiện tại mà chúng được xác định không có trong hợp đồng dẫn xuất từ ​​nó.

Thí dụ

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;
   
   function x() public returns (uint) {
      data = 3; // internal access
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); //external access
   }
}
contract D is C {
   function y() public returns (uint) {
      iData = 3; // internal access
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Nhà điều hành là gì?

Hãy để chúng tôi lấy một biểu thức đơn giản 4 + 5 is equal to 9. Ở đây 4 và 5 được gọi làoperands và '+' được gọi là operator. Solidity hỗ trợ các loại toán tử sau.

  • Toán tử số học
  • Toán tử so sánh
  • Toán tử logic (hoặc quan hệ)
  • Người điều hành nhiệm vụ
  • Toán tử có điều kiện (hoặc bậc ba)

Chúng ta hãy xem xét từng toán tử một.

Toán tử số học

Solidity hỗ trợ các toán tử số học sau:

Giả sử biến A giữ 10 và biến B giữ 20, thì -

Hiển thị ví dụ

Sr.No. Nhà điều hành & Mô tả
1

+ (Addition)

Thêm hai toán hạng

Ex: A + B sẽ cho 30

2

- (Subtraction)

Trừ toán hạng thứ hai với toán hạng đầu tiên

Ex: A - B sẽ cho -10

3

* (Multiplication)

Nhân cả hai toán hạng

Ex: A * B sẽ cho 200

4

/ (Division)

Chia tử số cho mẫu số

Ex: B / A sẽ cho 2

5

% (Modulus)

Kết quả còn lại của một phép chia số nguyên

Ex: B% A sẽ cho 0

6

++ (Increment)

Tăng giá trị số nguyên lên một

Ex: A ++ sẽ cho 11

7

-- (Decrement)

Giảm một giá trị số nguyên

Ex: A-- sẽ cho 9

Toán tử so sánh

Solidity hỗ trợ các toán tử so sánh sau:

Giả sử biến A giữ 10 và biến B giữ 20, thì -

Hiển thị ví dụ

Sr.No. Nhà điều hành & Mô tả
1

= = (Equal)

Kiểm tra xem giá trị của hai toán hạng có bằng nhau hay không, nếu có thì điều kiện trở thành true.

Ex: (A == B) không đúng.

2

!= (Not Equal)

Kiểm tra xem giá trị của hai toán hạng có bằng nhau hay không, nếu các giá trị không bằng nhau thì điều kiện trở thành true.

Ex: (A! = B) là đúng.

3

> (Greater than)

Kiểm tra xem giá trị của toán hạng bên trái có lớn hơn giá trị của toán hạng bên phải hay không, nếu có thì điều kiện trở thành true.

Ex: (A> B) không đúng.

4

< (Less than)

Kiểm tra xem giá trị của toán hạng bên trái có nhỏ hơn giá trị của toán hạng bên phải hay không, nếu có, thì điều kiện trở thành true.

Ex: (A <B) là đúng.

5

>= (Greater than or Equal to)

Kiểm tra xem giá trị của toán hạng bên trái có lớn hơn hoặc bằng giá trị của toán hạng bên phải hay không, nếu có thì điều kiện trở thành true.

Ex: (A> = B) là không đúng.

6

<= (Less than or Equal to)

Kiểm tra xem giá trị của toán hạng bên trái có nhỏ hơn hoặc bằng giá trị của toán hạng bên phải hay không, nếu có, thì điều kiện trở thành true.

Ex: (A <= B) là đúng.

Toán tử logic

Solidity hỗ trợ các toán tử logic sau:

Giả sử biến A giữ 10 và biến B giữ 20, thì -

Hiển thị ví dụ

Sr.No. Nhà điều hành & Mô tả
1

&& (Logical AND)

Nếu cả hai toán hạng đều khác 0, thì điều kiện trở thành true.

Ex: (A && B) là đúng.

2

|| (Logical OR)

Nếu bất kỳ toán hạng nào trong hai toán hạng khác 0, thì điều kiện trở thành true.

Ex: (A || B) là đúng.

3

! (Logical NOT)

Đảo ngược trạng thái logic của toán hạng của nó. Nếu một điều kiện là đúng, thì toán tử logic NOT sẽ làm cho nó sai.

Ex:! (A && B) là sai.

Toán tử Bitwise

Solidity hỗ trợ các toán tử bitwise sau:

Giả sử biến A giữ 2 và biến B giữ 3, khi đó -

Hiển thị ví dụ

Sr.No. Nhà điều hành & Mô tả
1

& (Bitwise AND)

Nó thực hiện phép toán Boolean AND trên mỗi bit của các đối số nguyên của nó.

Ex: (A & B) là 2.

2

| (BitWise OR)

Nó thực hiện phép toán Boolean OR trên mỗi bit của các đối số nguyên của nó.

Ex: (A | B) là 3.

3

^ (Bitwise XOR)

Nó thực hiện phép toán HOẶC độc quyền Boolean trên mỗi bit của các đối số nguyên của nó. HOẶC độc quyền có nghĩa là toán hạng một là đúng hoặc toán hạng hai là đúng, nhưng không phải cả hai.

Ex: (A ^ B) là 1.

4

~ (Bitwise Not)

Nó là một toán tử một ngôi và hoạt động bằng cách đảo ngược tất cả các bit trong toán hạng.

Ex: (~ B) là -4.

5

<< (Left Shift)

Nó di chuyển tất cả các bit trong toán hạng đầu tiên của nó sang trái theo số vị trí được chỉ định trong toán hạng thứ hai. Các bit mới được lấp đầy bởi các số không. Chuyển một giá trị sang trái một vị trí tương đương với nhân nó với 2, dịch chuyển hai vị trí tương đương với nhân 4, v.v.

Ex: (A << 1) là 4.

6

>> (Right Shift)

Toán tử Shift phải nhị phân. Giá trị của toán hạng bên trái được di chuyển sang phải bằng số bit được chỉ định bởi toán hạng bên phải.

Ex: (A >> 1) là 1.

7

>>> (Right shift with Zero)

Toán tử này cũng giống như toán tử >>, ngoại trừ việc các bit được chuyển sang bên trái luôn bằng không.

Ex: (A >>> 1) là 1.

Người điều hành nhiệm vụ

Solidity hỗ trợ các toán tử gán sau:

Hiển thị ví dụ

Sr.No. Nhà điều hành & Mô tả
1

= (Simple Assignment )

Gán các giá trị từ toán hạng bên phải cho toán hạng bên trái

Ex: C = A + B sẽ gán giá trị của A + B vào C

2

+= (Add and Assignment)

Nó thêm toán hạng bên phải vào toán hạng bên trái và gán kết quả cho toán hạng bên trái.

Ex: C + = A tương đương với C = C + A

3

−= (Subtract and Assignment)

Nó trừ toán hạng bên phải khỏi toán hạng bên trái và gán kết quả cho toán hạng bên trái.

Ex: C - = A tương đương với C = C - A

4

*= (Multiply and Assignment)

Nó nhân toán hạng bên phải với toán hạng bên trái và gán kết quả cho toán hạng bên trái.

Ex: C * = A tương đương với C = C * A

5

/= (Divide and Assignment)

Nó chia toán hạng bên trái với toán hạng bên phải và gán kết quả cho toán hạng bên trái.

Ex: C / = A tương đương với C = C / A

6

%= (Modules and Assignment)

Nó có mô đun sử dụng hai toán hạng và gán kết quả cho toán hạng bên trái.

Ex: C% = A tương đương với C = C% A

Note - Logic tương tự áp dụng cho các toán tử Bitwise vì vậy chúng sẽ trở thành như << =, >> =, >> =, & =, | = và ^ =.

Điều hành có điều kiện (? :)

Toán tử điều kiện đầu tiên đánh giá một biểu thức cho một giá trị đúng hoặc sai và sau đó thực hiện một trong hai câu lệnh đã cho tùy thuộc vào kết quả của việc đánh giá.

Hiển thị ví dụ

Sr.No. Nhà điều hành và Mô tả
1

? : (Conditional )

Nếu Điều kiện là đúng? Sau đó giá trị X: Ngược lại giá trị Y

Trong khi viết hợp đồng, bạn có thể gặp phải tình huống cần thực hiện lại nhiều lần một hành động. Trong những tình huống như vậy, bạn sẽ cần viết các câu lệnh lặp để giảm số dòng.

Solidity hỗ trợ tất cả các vòng lặp cần thiết để giảm bớt áp lực lập trình.

Sr.No Vòng lặp & Mô tả
1

Trong khi lặp lại

Vòng lặp cơ bản nhất trong Solidity là vòng lặp while sẽ được thảo luận trong chương này.

2

làm ... trong khi Vòng lặp

Vòng lặp do ... while tương tự như vòng lặp while ngoại trừ việc kiểm tra điều kiện xảy ra ở cuối vòng lặp.

3

Đối với vòng lặp

Vòng lặp for là hình thức lặp lại nhỏ gọn nhất. Nó bao gồm ba phần quan trọng sau đây.

4

Kiểm soát vòng lặp

Solidity cung cấp toàn quyền kiểm soát để xử lý các vòng lặp và các câu lệnh chuyển đổi.

Trong khi viết một chương trình, có thể có một tình huống khi bạn cần áp dụng một trong số các đường dẫn nhất định. Trong những trường hợp như vậy, bạn cần sử dụng các câu lệnh điều kiện cho phép chương trình của bạn đưa ra quyết định chính xác và thực hiện các hành động đúng.

Solidity hỗ trợ các câu lệnh có điều kiện được sử dụng để thực hiện các hành động khác nhau dựa trên các điều kiện khác nhau. Ở đây chúng tôi sẽ giải thíchif..else tuyên bố.

Lưu đồ của if-else

Lưu đồ sau đây cho thấy cách hoạt động của câu lệnh if-else.

Solidity hỗ trợ các hình thức sau if..else tuyên bố -

Sr.No Tuyên bố & Mô tả
1

câu lệnh if

Câu lệnh if là câu lệnh điều khiển cơ bản cho phép Solidity đưa ra quyết định và thực hiện các câu lệnh một cách có điều kiện.

2

câu lệnh if ... else

Câu lệnh 'if ... else' là dạng câu lệnh điều khiển tiếp theo cho phép Solidity thực thi các câu lệnh theo cách được kiểm soát nhiều hơn.

3

if ... else if ... câu lệnh.

Câu lệnh if ... else if ... là một dạng nâng cao của if ... else cho phép Solidity đưa ra quyết định đúng trong một số điều kiện.

Solidity hỗ trợ chuỗi ký tự bằng cách sử dụng cả dấu ngoặc kép (") và dấu nháy đơn ('). Nó cung cấp chuỗi làm kiểu dữ liệu để khai báo một biến kiểu String.

pragma solidity ^0.5.0;

contract SolidityTest {
   string data = "test";
}

Trong ví dụ trên, "test" là một chuỗi ký tự và dữ liệu là một biến chuỗi. Cách ưu tiên hơn là sử dụng các kiểu byte thay vì Chuỗi vì hoạt động chuỗi đòi hỏi nhiều gas hơn so với hoạt động byte. Solidity cung cấp chuyển đổi sẵn có giữa các byte thành chuỗi và ngược lại. Trong Solidity, chúng ta có thể gán chuỗi ký tự cho một biến kiểu byte32 một cách dễ dàng. Solidity coi nó như một ký tự byte32.

pragma solidity ^0.5.0;

contract SolidityTest {
   bytes32 data = "test";
}

Nhân vật thoát

Sr.No. Nhân vật & Mô tả
1

\n

Bắt đầu một dòng mới.

2

\\

Gạch chéo ngược

3

\'

Trích dẫn đơn

4

\"

Trích dẫn kép

5

\b

Backspace

6

\f

Thức ăn dạng

7

\r

Vận chuyển trở lại

số 8

\t

Chuyển hướng

9

\v

Tab dọc

10

\xNN

Đại diện cho giá trị Hex và chèn các byte thích hợp.

11

\uNNNN

Đại diện cho giá trị Unicode và chèn chuỗi UTF-8.

Chuyển đổi byte sang chuỗi

Các byte có thể được chuyển đổi thành Chuỗi bằng cách sử dụng hàm tạo string ().

bytes memory bstr = new bytes(10);
string message = string(bstr);

Thí dụ

Hãy thử đoạn mã sau để hiểu cách chuỗi hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: string: 3

Mảng là một cấu trúc dữ liệu, nó lưu trữ một tập hợp tuần tự có kích thước cố định của các phần tử cùng kiểu. Mảng được sử dụng để lưu trữ một tập hợp dữ liệu, nhưng thường hữu ích hơn nếu coi một mảng là một tập hợp các biến cùng kiểu.

Thay vì khai báo các biến riêng lẻ, chẳng hạn như number0, number1, ... và number99, bạn khai báo một biến mảng chẳng hạn như số và sử dụng số [0], số [1] và ..., số [99] để biểu diễn các biến riêng lẻ. Một phần tử cụ thể trong một mảng được truy cập bởi một chỉ mục.

Trong Solidity, một mảng có thể có kích thước cố định theo thời gian biên dịch hoặc kích thước động. Đối với mảng lưu trữ, nó cũng có thể có nhiều loại phần tử khác nhau. Trong trường hợp mảng bộ nhớ, kiểu phần tử không thể được ánh xạ và trong trường hợp nó được sử dụng làm tham số hàm thì kiểu phần tử phải là kiểu ABI.

Tất cả các mảng bao gồm các vị trí bộ nhớ liền kề. Địa chỉ thấp nhất tương ứng với phần tử đầu tiên và địa chỉ cao nhất cho phần tử cuối cùng.

Khai báo Mảng

Để khai báo một mảng có kích thước cố định trong Solidity, lập trình viên chỉ định kiểu của các phần tử và số phần tử theo yêu cầu của một mảng như sau:

type arrayName [ arraySize ];

Đây được gọi là mảng một chiều. CácarraySize phải là một hằng số nguyên lớn hơn 0 và typecó thể là bất kỳ kiểu dữ liệu Solidity hợp lệ nào. Ví dụ, để khai báo một mảng 10 phần tử được gọi là số dư kiểu uint, hãy sử dụng câu lệnh này:

uint balance[10];

Để khai báo một mảng có kích thước động trong Solidity, lập trình viên chỉ định kiểu của các phần tử như sau:

type[] arrayName;

Khởi tạo Mảng

Bạn có thể khởi tạo từng phần tử mảng Solidity hoặc sử dụng một câu lệnh như sau:

uint balance[3] = [1, 2, 3];

Số lượng giá trị giữa dấu ngoặc [] không được lớn hơn số phần tử mà chúng ta khai báo cho mảng giữa dấu ngoặc vuông []. Sau đây là một ví dụ để gán một phần tử duy nhất của mảng:

Nếu bạn bỏ qua kích thước của mảng, một mảng vừa đủ lớn để chứa quá trình khởi tạo sẽ được tạo. Do đó, nếu bạn viết -

uint balance[] = [1, 2, 3];

Bạn sẽ tạo chính xác mảng giống như bạn đã làm trong ví dụ trước.

balance[2] = 5;

Các chuyển nhượng tuyên bố số lượng phần tử 3 trên thứ trong mảng giá trị 5.

Tạo mảng bộ nhớ động

Mảng bộ nhớ động được tạo bằng từ khóa mới.

uint size = 3;
uint balance[] = new uint[](size);

Truy cập các phần tử mảng

Một phần tử được truy cập bằng cách lập chỉ mục tên mảng. Điều này được thực hiện bằng cách đặt chỉ số của phần tử trong dấu ngoặc vuông sau tên của mảng. Ví dụ -

uint salary = balance[2];

Câu lệnh trên sẽ lấy phần tử thứ 3 từ mảng và gán giá trị cho biến lương. Sau đây là một ví dụ, sẽ sử dụng tất cả ba khái niệm được đề cập ở trên viz. khai báo, gán và truy cập mảng -

Các thành viên

  • length- length trả về kích thước của mảng. chiều dài có thể được sử dụng để thay đổi kích thước của mảng động được thiết lập nó.

  • push- push cho phép nối một phần tử vào một mảng lưu trữ động ở cuối. Nó trả về độ dài mới của mảng.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách các mảng hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract test {
   function testArray() public pure{
      uint len = 7; 
      
      //dynamic array
      uint[] memory a = new uint[](7);
      
      //bytes is same as byte[]
      bytes memory b = new bytes(len);
      
      assert(a.length == 7);
      assert(b.length == len);
      
      //access array variable
      a[6] = 8;
      
      //test array variable
      assert(a[6] == 8);
      
      //static array
      uint[3] memory c = [uint(1) , 2, 3];
      assert(c.length == 3);
   }
}

Enums hạn chế một biến chỉ có một trong một số giá trị được xác định trước. Các giá trị trong danh sách được liệt kê này được gọi là enum.

Với việc sử dụng enum, có thể giảm số lượng lỗi trong mã của bạn.

Ví dụ: nếu chúng tôi xem xét ứng dụng cho một cửa hàng nước trái cây tươi, có thể giới hạn kích thước ly ở mức nhỏ, vừa và lớn. Điều này sẽ đảm bảo rằng nó sẽ không cho phép bất kỳ ai đặt hàng bất kỳ kích thước nào khác ngoài nhỏ, trung bình hoặc lớn.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách hoạt động của enum trong Solidity.

pragma solidity ^0.5.0;

contract test {
   enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
   FreshJuiceSize choice;
   FreshJuiceSize constant defaultChoice = FreshJuiceSize.MEDIUM;

   function setLarge() public {
      choice = FreshJuiceSize.LARGE;
   }
   function getChoice() public view returns (FreshJuiceSize) {
      return choice;
   }
   function getDefaultChoice() public pure returns (uint) {
      return uint(defaultChoice);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Lần nhấp đầu tiên setLarge Nút để đặt giá trị là LARGE sau đó nhấp vào getChoice để có được sự lựa chọn đã chọn.

Đầu ra

uint8: 2

Nhấp chuột getDefaultChoice Nút để có lựa chọn mặc định.

Đầu ra

uint256: 1

Các kiểu cấu trúc được sử dụng để biểu diễn một bản ghi. Giả sử bạn muốn theo dõi sách của mình trong thư viện. Bạn có thể muốn theo dõi các thuộc tính sau về mỗi cuốn sách -

  • Title
  • Author
  • Subject
  • ID sách

Xác định một cấu trúc

Để xác định một Cấu trúc, bạn phải sử dụng structtừ khóa. Từ khóa struct xác định một kiểu dữ liệu mới, có nhiều hơn một thành viên. Định dạng của câu lệnh struct như sau:

struct struct_name { 
   type1 type_name_1;
   type2 type_name_2;
   type3 type_name_3;
}

Thí dụ

struct Book { 
   string title;
   string author;
   uint book_id;
}

Truy cập một cấu trúc và biến của nó

Để truy cập bất kỳ thành viên nào của một cấu trúc, chúng tôi sử dụng toán tử truy cập thành viên (.). Toán tử truy cập thành viên được mã hóa là dấu chấm giữa tên biến cấu trúc và thành viên cấu trúc mà chúng ta muốn truy cập. Bạn sẽ sử dụng cấu trúc để xác định các biến của kiểu cấu trúc. Ví dụ sau đây cho thấy cách sử dụng một cấu trúc trong một chương trình.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách cấu trúc hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract test {
   struct Book { 
      string title;
      string author;
      uint book_id;
   }
   Book book;

   function setBook() public {
      book = Book('Learn Java', 'TP', 1);
   }
   function getBookId() public view returns (uint) {
      return book.book_id;
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Lần nhấp đầu tiên setBook Nút để đặt giá trị là LARGE sau đó nhấp vào getBookId để lấy id sách đã chọn.

Đầu ra

uint256: 1

Ánh xạ là một kiểu tham chiếu dưới dạng mảng và cấu trúc. Sau đây là cú pháp để khai báo một kiểu ánh xạ.

mapping(_KeyType => _ValueType)

Ở đâu

  • _KeyType- có thể là bất kỳ kiểu tích hợp nào cộng với byte và chuỗi. Không cho phép loại tham chiếu hoặc các đối tượng phức tạp.

  • _ValueType - có thể là bất kỳ loại nào.

Cân nhắc

  • Ánh xạ chỉ có thể có loại storage và thường được sử dụng cho các biến trạng thái.

  • Ánh xạ có thể được đánh dấu công khai. Solidity tự động tạo getter cho nó.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách hoạt động của kiểu ánh xạ trong Solidity.

pragma solidity ^0.5.0;

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

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Lần nhấp đầu tiên updateBalance Nút để đặt giá trị là 10 sau đó nhìn vào nhật ký sẽ hiển thị đầu ra được giải mã dưới dạng:

Đầu ra

{
   "0": "uint256: 10"
}

Solidity cho phép chuyển đổi ngầm định cũng như rõ ràng. Trình biên dịch Solidity cho phép chuyển đổi ngầm giữa hai kiểu dữ liệu với điều kiện không thể chuyển đổi ngầm và không mất thông tin. Ví dụ: uint8 có thể chuyển đổi thành uint16 nhưng int8 có thể chuyển đổi thành uint256 vì int8 có thể chứa giá trị âm không được phép trong uint256.

Chuyển đổi rõ ràng

Chúng ta có thể chuyển đổi một cách rõ ràng một kiểu dữ liệu sang kiểu khác bằng cách sử dụng cú pháp của hàm tạo.

int8 y = -3;
uint x = uint(y);
//Now x = 0xfffff..fd == two complement representation of -3 in 256 bit format.

Việc chuyển đổi sang loại nhỏ hơn sẽ tốn bit thứ tự cao hơn.

uint32 a = 0x12345678;
uint16 b = uint16(a); // b = 0x5678

Việc chuyển đổi sang loại cao hơn sẽ thêm các bit đệm ở bên trái.

uint16 a = 0x1234;
uint32 b = uint32(a); // b = 0x00001234

Việc chuyển đổi sang byte nhỏ hơn sẽ tốn dữ liệu thứ tự cao hơn.

bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b = 0x12

Chuyển đổi sang byte lớn hơn thêm các bit đệm ở bên phải.

bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b = 0x12340000

Chuyển đổi giữa byte kích thước cố định và int chỉ có thể thực hiện được khi cả hai đều có cùng kích thước.

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

Số thập lục phân có thể được gán cho bất kỳ kiểu số nguyên nào nếu không cần cắt bớt.

uint8 a = 12; // no error
uint32 b = 1234; // no error
uint16 c = 0x123456; // error, as truncation required to 0x3456

Về độ vững chắc, chúng ta có thể sử dụng wei, finney, szabo hoặc ether làm hậu tố cho một nghĩa đen được sử dụng để chuyển đổi các mệnh giá khác nhau dựa trên ether. Đơn vị thấp nhất là wei và 1e12 đại diện cho 1 x 10 12 .

assert(1 wei == 1);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);
assert(2 ether == 2000 fenny);

Đơn vị thời gian

Tương tự như tiền tệ, Solidity có đơn vị thời gian trong đó đơn vị thấp nhất là thứ hai và chúng ta có thể sử dụng giây, phút, giờ, ngày và tuần làm hậu tố để biểu thị thời gian.

assert(1 seconds == 1);
assert(1 minutes == 60 seconds);
assert(1 hours == 60 minutes);
assert(1 day == 24 hours);
assert(1 week == 7 days);

Các biến đặc biệt là các biến có sẵn trên toàn cầu và cung cấp thông tin về blockchain. Sau đây là danh sách các biến đặc biệt:

Sr.No. Biến & Mô tả Đặc biệt
1

blockhash(uint blockNumber) returns (bytes32)

Hash của khối đã cho - chỉ hoạt động cho 256 khối gần đây nhất, không bao gồm khối hiện tại.

2

block.coinbase (address payable)

Địa chỉ của người khai thác khối hiện tại.

3

block.difficulty (uint)

khó khăn khối hiện tại.

4

block.gaslimit (uint)

Gaslimit khối hiện tại.

5

block.number (uint)

Số khối hiện tại.

6

block.timestamp

Dấu thời gian khối hiện tại dưới dạng giây kể từ kỷ nguyên unix.

7

gasleft() returns (uint256)

Khí còn lại.

số 8

msg.data (bytes calldata)

Calldata hoàn chỉnh.

9

msg.sender (address payable)

Người gửi tin nhắn (cuộc gọi hiện tại).

10

msg.sig (bytes4)

Bốn byte đầu tiên của calldata (tức là mã định danh hàm)

11

msg.value (uint)

Số wei đã gửi cùng với tin nhắn.

12

now (uint)

Dấu thời gian khối hiện tại (bí danh cho block.timestamp).

13

tx.gasprice (uint)

Giá gas của giao dịch.

14

tx.origin (address payable)

Người gửi giao dịch (chuỗi cuộc gọi đầy đủ).

Thí dụ

Hãy thử đoạn mã sau để xem việc sử dụng msg, một biến đặc biệt để lấy địa chỉ người gửi trong Solidity.

pragma solidity ^0.5.0;

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

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Lần nhấp đầu tiên updateBalance Nút để đặt giá trị là 10 sau đó nhìn vào nhật ký sẽ hiển thị đầu ra được giải mã dưới dạng:

Đầu ra

{
   "0": "uint256: 10"
}

Style Guide giúp duy trì bố cục mã nhất quán và làm cho mã dễ đọc hơn. Sau đây là các phương pháp hay nhất sau đây khi viết hợp đồng với Solidity.

Bố cục mã

  • Indentation- Sử dụng 4 dấu cách thay vì tab để duy trì mức độ thụt lề. Tránh trộn dấu cách với các tab.

  • Two Blank Lines Rule - Sử dụng 2 Dòng trống giữa hai định nghĩa hợp đồng.

pragma solidity ^0.5.0;

contract LedgerBalance {
   //...
}
contract Updater {
   //...
}
  • One Blank Line Rule- Sử dụng 1 Dòng trống giữa hai chức năng. Trường hợp chỉ khai báo thì không cần để trống dòng.

pragma solidity ^0.5.0;

contract A {
   function balance() public pure;
   function account() public pure;
}
contract B is A {
   function balance() public pure {
      // ...
   }
   function account() public pure {
      // ...
   }
}
  • Maximum Line Length - Một dòng không được cắt ngang 79 ký tự để người đọc dễ phân tích mã.

  • Wrapping rules- Đối số đầu tiên ở dòng mới không mở ngoặc đơn. Sử dụng một thụt lề cho mỗi đối số. Phần tử kết thúc); nên là người cuối cùng.

function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
variable = function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
event multipleArguments(
   address sender,
   address recipient,
   uint256 publicKey,
   uint256 amount,
   bytes32[] options
);
MultipleArguments(
   sender,
   recipient,
   publicKey,
   amount,
   options
);
  • Source Code Encoding - Tốt nhất nên sử dụng mã hóa UTF-8 hoặc ASCII.

  • Imports - Các câu lệnh nhập phải được đặt ở đầu tệp ngay sau khai báo pragma.

  • Order of Functions - Các chức năng nên được nhóm lại theo khả năng hiển thị của chúng.

pragma solidity ^0.5.0;

contract A {
   constructor() public {
      // ...
   }
   function() external {
      // ...
   }

   // External functions
   // ...

   // External view functions
   // ...

   // External pure functions 
   // ...

   // Public functions
   // ...

   // Internal functions
   // ...

   // Private functions
   // ...
}
  • Avoid extra whitespaces - Tránh các khoảng trắng ngay bên trong dấu ngoặc đơn, dấu ngoặc hoặc dấu ngoặc nhọn.

  • Control structures- Dấu ngoặc nhọn phải mở trên cùng dòng với khai báo. Đóng trên dòng riêng của họ duy trì cùng một vết lõm. Sử dụng khoảng trống có dấu ngoặc nhọn.

pragma solidity ^0.5.0;

contract Coin {
   struct Bank {
      address owner;
      uint balance;
   }
}
if (x < 3) {
   x += 1;
} else if (x > 7) {
   x -= 1;
} else {
   x = 5;
}
if (x < 3)
   x += 1;
else
   x -= 1;
  • Function Declaration- Sử dụng quy tắc trên để niềng răng. Luôn thêm nhãn hiển thị. Nhãn hiển thị phải xuất hiện đầu tiên trước bất kỳ công cụ sửa đổi tùy chỉnh nào.

function kill() public onlyowner {
   selfdestruct(owner);
}
  • Mappings - Tránh các khoảng trắng trong khi khai báo các biến ánh xạ.

mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
  • Variable declaration - Tránh khoảng trắng trong khi khai báo biến mảng.

uint[] x;  // not unit [] x;
  • String declaration - Sử dụng dấu ngoặc kép để khai báo một chuỗi thay vì dấu nháy đơn.

str = "foo";
str = "Hamlet says, 'To be or not to be...'";

Thứ tự của bố cục

Các phần tử nên được bố trí theo thứ tự sau.

  • Tuyên bố Pragma

  • Nhập báo cáo

  • Interfaces

  • Libraries

  • Contracts

Trong Interfaces, thư viện hoặc hợp đồng, thứ tự phải là:

  • Nhập các khai báo

  • Biến trạng thái

  • Events

  • Functions

Quy ước đặt tên

  • Hợp đồng và Thư viện phải được đặt tên bằng cách sử dụng Cap AdWords Style. Ví dụ: SmartContract, Chủ sở hữu, v.v.

  • Hợp đồng và tên Thư viện phải khớp với tên tệp của chúng.

  • Trong trường hợp có nhiều hợp đồng / thư viện trong một tệp, hãy sử dụng tên của hợp đồng / thư viện lõi.

Owned.sol

pragma solidity ^0.5.0;

// Owned.sol
contract Owned {
   address public owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      //....
   }
   function transferOwnership(address newOwner) public onlyOwner {
      //...
   }
}

Congress.sol

pragma solidity ^0.5.0;

// Congress.sol
import "./Owned.sol";

contract Congress is Owned, TokenRecipient {
   //...
}
  • Tên cấu trúc

    - Sử dụng Cap AdWords Style như SmartCoin.

  • Tên sự kiện

    - Sử dụng Cap AdWords Style như Deposit, AfterTransfer.

  • Tên hàm

    - Sử dụng MixedCase Style giống như InitiateSupply.

  • Biến cục bộ và biến trạng thái

    - Sử dụng kiểu mixCase như createAddress, cung cấp.

  • Hằng số

    - Sử dụng tất cả các chữ cái viết hoa với dấu gạch dưới để tách các từ như MAX_BLOCKS.

  • Tên bổ trợ

    - Sử dụng MixCase Style như onlyAfter.

  • Enum Names

    - Sử dụng kiểu Cap AdWords như TokenGroup.

Hàm là một nhóm mã có thể sử dụng lại có thể được gọi ở bất kỳ đâu trong chương trình của bạn. Điều này giúp loại bỏ nhu cầu viết đi viết lại cùng một đoạn mã. Nó giúp các lập trình viên viết mã mô-đun. Các hàm cho phép người lập trình chia một chương trình lớn thành một số hàm nhỏ và có thể quản lý được.

Giống như bất kỳ ngôn ngữ lập trình nâng cao nào khác, Solidity cũng hỗ trợ tất cả các tính năng cần thiết để viết mã mô-đun bằng các hàm. Phần này giải thích cách viết các hàm của riêng bạn trong Solidity.

Định nghĩa hàm

Trước khi sử dụng một hàm, chúng ta cần xác định nó. Cách phổ biến nhất để xác định một hàm trong Solidity là sử dụngfunction từ khóa, theo sau là tên hàm duy nhất, danh sách các tham số (có thể trống) và khối câu lệnh được bao quanh bởi dấu ngoặc nhọn.

Cú pháp

Cú pháp cơ bản được hiển thị ở đây.

function function-name(parameter-list) scope returns() {
   //statements
}

Thí dụ

Hãy thử ví dụ sau. Nó định nghĩa một hàm được gọi là getResult không có tham số -

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Gọi một hàm

Để gọi một hàm ở đâu đó sau này trong Hợp đồng, bạn chỉ cần viết tên của hàm đó như được hiển thị trong đoạn mã sau.

Hãy thử đoạn mã sau để hiểu cách chuỗi hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: string: 3

Tham số chức năng

Cho đến bây giờ, chúng ta đã thấy các hàm không có tham số. Nhưng có một cơ sở để truyền các tham số khác nhau trong khi gọi một hàm. Các tham số được truyền này có thể được nắm bắt bên trong hàm và bất kỳ thao tác nào có thể được thực hiện trên các tham số đó. Một hàm có thể nhận nhiều tham số được phân tách bằng dấu phẩy.

Thí dụ

Hãy thử ví dụ sau. Chúng tôi đã sử dụng mộtuint2strchức năng ở đây. Nó có một tham số.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: string: 3

Tuyên bố trở lại

Một hàm Solidity có thể có một tùy chọn returntuyên bố. Điều này là bắt buộc nếu bạn muốn trả về giá trị từ một hàm. Câu lệnh này phải là câu lệnh cuối cùng trong một hàm.

Như trong ví dụ trên, chúng ta đang sử dụng hàm uint2str để trả về một chuỗi.

Trong Solidity, một hàm cũng có thể trả về nhiều giá trị. Xem ví dụ bên dưới -

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b;
  
      //alternative return statement to return 
      //multiple values
      //return(a*b, a+b);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: uint256: product 2
1: uint256: sum 3

Hàm sửa đổi được sử dụng để sửa đổi hành vi của một hàm. Ví dụ để thêm điều kiện tiên quyết vào một hàm.

Đầu tiên, chúng tôi tạo một công cụ sửa đổi có hoặc không có tham số.

contract Owner {
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}

Phần thân hàm được chèn vào nơi ký hiệu đặc biệt "_;" xuất hiện trong định nghĩa của một bổ ngữ. Vì vậy, nếu điều kiện của modifier được thỏa mãn trong khi gọi hàm này, thì hàm được thực thi và ngược lại, một ngoại lệ được ném ra.

Xem ví dụ bên dưới -

pragma solidity ^0.5.0;

contract Owner {
   address owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}
contract Register is Owner {
   mapping (address => bool) registeredAddresses;
   uint price;
   constructor(uint initialPrice) public { price = initialPrice; }
   
   function register() public payable costs(price) {
      registeredAddresses[msg.sender] = true;
   }
   function changePrice(uint _price) public onlyOwner {
      price = _price;
   }
}

Các hàm xem đảm bảo rằng chúng sẽ không sửa đổi trạng thái. Một hàm có thể được khai báo làview. Các câu lệnh sau nếu có trong hàm được coi là sửa đổi trạng thái và trình biên dịch sẽ đưa ra cảnh báo trong những trường hợp như vậy.

  • Sửa đổi các biến trạng thái.

  • Sự kiện phát ra.

  • Tạo các hợp đồng khác.

  • Sử dụng selfdestruct.

  • Gửi Ether qua cuộc gọi.

  • Gọi bất kỳ chức năng nào không được đánh dấu xem hoặc thuần túy.

  • Sử dụng các cuộc gọi cấp thấp.

  • Sử dụng lắp ráp nội tuyến có chứa các mã quang nhất định.

Phương thức Getter là các chức năng xem mặc định.

Xem ví dụ bên dưới bằng cách sử dụng chức năng xem.

Thí dụ

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: uint256: product 2
1: uint256: sum 3

Các chức năng thuần túy đảm bảo rằng chúng không đọc hoặc sửa đổi trạng thái. Một hàm có thể được khai báo làpure. Các câu lệnh sau nếu có trong hàm được coi là đang đọc trạng thái và trình biên dịch sẽ đưa ra cảnh báo trong những trường hợp như vậy.

  • Đọc các biến trạng thái.

  • Địa chỉ truy cập (this) .balance hoặc <address> .balance.

  • Truy cập bất kỳ biến đặc biệt nào của khối, tx, msg (có thể đọc msg.sig và msg.data).

  • Gọi bất kỳ chức năng nào không được đánh dấu thuần túy.

  • Sử dụng lắp ráp nội tuyến có chứa các mã quang nhất định.

Các hàm thuần túy có thể sử dụng các hàm revert () và request () để hoàn nguyên các thay đổi trạng thái tiềm ẩn nếu có lỗi xảy ra.

Xem ví dụ bên dưới bằng cách sử dụng chức năng xem.

Thí dụ

pragma solidity ^0.5.0;

contract Test {
   function getResult() public pure returns(uint product, uint sum){
      uint a = 1; 
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: uint256: product 2
1: uint256: sum 3

Chức năng dự phòng là một chức năng đặc biệt có sẵn cho một hợp đồng. Nó có các tính năng sau:

  • Nó được gọi khi một hàm không tồn tại được gọi trên hợp đồng.

  • Nó được yêu cầu phải được đánh dấu bên ngoài.

  • Nó không có tên.

  • Nó không có đối số

  • Nó không thể trả lại bất kỳ điều gì.

  • Nó có thể được xác định một trong mỗi hợp đồng.

  • Nếu không được đánh dấu là phải trả, nó sẽ ném ra ngoại lệ nếu hợp đồng nhận được ether thuần túy mà không có dữ liệu.

Ví dụ sau đây cho thấy khái niệm về hàm dự phòng cho mỗi hợp đồng.

Thí dụ

pragma solidity ^0.5.0;

contract Test {
   uint public x ;
   function() external { x = 1; }    
}
contract Sink {
   function() external payable { }
}
contract Caller {
   function callTest(Test test) public returns (bool) {
      (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
      require(success);
      // test.x is now 1

      address payable testPayable = address(uint160(address(test)));

      // Sending ether to Test contract,
      // the transfer will fail, i.e. this returns false here.
      return (testPayable.send(2 ether));
   }
   function callSink(Sink sink) public returns (bool) {
      address payable sinkPayable = address(sink);
      return (sinkPayable.send(2 ether));
   }
}

Bạn có thể có nhiều định nghĩa cho cùng một tên hàm trong cùng một phạm vi. Định nghĩa của hàm phải khác nhau theo kiểu và / hoặc số lượng đối số trong danh sách đối số. Bạn không thể nạp chồng các khai báo hàm chỉ khác nhau theo kiểu trả về.

Ví dụ sau đây cho thấy khái niệm nạp chồng hàm trong Solidity.

Thí dụ

pragma solidity ^0.5.0;

contract Test {
   function getSum(uint a, uint b) public pure returns(uint){      
      return a + b;
   }
   function getSum(uint a, uint b, uint c) public pure returns(uint){      
      return a + b + c;
   }
   function callSumWithTwoArguments() public pure returns(uint){
      return getSum(1,2);
   }
   function callSumWithThreeArguments() public pure returns(uint){
      return getSum(1,2,3);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Nhấp vào nút callSumWithTwoArguments đầu tiên và sau đó nhấp vào nút callSumWithThreeArguments để xem kết quả.

Đầu ra

0: uint256: 3
0: uint256: 6

Solidity cũng cung cấp các hàm toán học có sẵn. Sau đây là các phương pháp được sử dụng nhiều:

  • addmod(uint x, uint y, uint k) returns (uint)- computes (x + y)% k trong đó phép cộng được thực hiện với độ chính xác tùy ý và không bao quanh 2 256 .

  • mulmod(uint x, uint y, uint k) returns (uint)- computes (x * y)% k trong đó phép cộng được thực hiện với độ chính xác tùy ý và không bao quanh 2 256 .

Ví dụ sau cho thấy cách sử dụng các hàm toán học trong Solidity.

Thí dụ

pragma solidity ^0.5.0;

contract Test {   
   function callAddMod() public pure returns(uint){
      return addmod(4, 5, 3);
   }
   function callMulMod() public pure returns(uint){
      return mulmod(4, 5, 3);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Nhấp vào nút callAddMod trước và sau đó nhấp vào nút callMulMod để xem kết quả.

Đầu ra

0: uint256: 0
0: uint256: 2

Solidity cũng cung cấp các chức năng mật mã sẵn có. Sau đây là các phương pháp quan trọng -

  • keccak256(bytes memory) returns (bytes32) - tính toán băm Keccak-256 của đầu vào.

  • sha256(bytes memory) returns (bytes32) - tính toán mã băm SHA-256 của đầu vào.

  • ripemd160(bytes memory) returns (bytes20) - tính toán băm RIPEMD-160 của đầu vào.

  • sha256(bytes memory) returns (bytes32) - tính toán mã băm SHA-256 của đầu vào.

  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)- khôi phục địa chỉ được liên kết với khóa công khai từ chữ ký đường cong elliptic hoặc trả về số không khi bị lỗi. Các tham số hàm tương ứng với các giá trị ECDSA của chữ ký: r - 32 byte đầu tiên của chữ ký; s: 32 byte thứ hai của chữ ký; v: 1 byte cuối cùng của chữ ký. Phương thức này trả về một địa chỉ.

Ví dụ sau cho thấy việc sử dụng hàm mật mã trong Solidity.

Thí dụ

pragma solidity ^0.5.0;

contract Test {   
   function callKeccak256() public pure returns(bytes32 result){
      return keccak256("ABC");
   }  
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8

Mô hình rút tiền đảm bảo rằng cuộc gọi chuyển khoản trực tiếp không được thực hiện gây ra mối đe dọa bảo mật. Hợp đồng sau đây cho thấy việc sử dụng không an toàn cuộc gọi chuyển tiền để gửi ether.

pragma solidity ^0.5.0;

contract Test {
   address payable public richest;
   uint public mostSent;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         // Insecure practice
         richest.transfer(msg.value);
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
}

Hợp đồng trên có thể được hiển thị ở trạng thái không sử dụng được bằng cách khiến hợp đồng giàu nhất trở thành hợp đồng không hoạt động chức năng dự phòng. Khi hàm dự phòng không thành công, hàm trở thànhRichest () cũng không thành công và hợp đồng sẽ bị mắc kẹt mãi mãi. Để giảm thiểu vấn đề này, chúng ta có thể sử dụng Mô hình Rút tiền.

Trong mô hình rút tiền, chúng tôi sẽ đặt lại số tiền đang chờ xử lý trước mỗi lần chuyển. Nó sẽ đảm bảo rằng chỉ có hợp đồng người gọi không thành công.

pragma solidity ^0.5.0;

contract Test {
   address public richest;
   uint public mostSent;

   mapping (address => uint) pendingWithdrawals;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         pendingWithdrawals[richest] += msg.value;
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
   function withdraw() public {
      uint amount = pendingWithdrawals[msg.sender];
      pendingWithdrawals[msg.sender] = 0;
      msg.sender.transfer(amount);
   }
}

Quyền truy cập hạn chế vào hợp đồng là một thực tế phổ biến. Theo Mặc định, trạng thái hợp đồng là chỉ đọc trừ khi nó được chỉ định là công khai.

Chúng tôi có thể hạn chế ai có thể sửa đổi trạng thái của hợp đồng hoặc gọi các chức năng của hợp đồng bằng cách sử dụng các công cụ sửa đổi. Chúng tôi sẽ tạo và sử dụng nhiều công cụ sửa đổi như được giải thích bên dưới -

  • onlyBy - khi được sử dụng trên một hàm thì chỉ người gọi được đề cập mới có thể gọi hàm này.

  • onlyAfter - Một khi được sử dụng trên một hàm thì hàm đó có thể được gọi sau một khoảng thời gian nhất định.

  • costs - khi được sử dụng trên một hàm thì người gọi chỉ có thể gọi hàm này nếu giá trị nhất định được cung cấp.

Thí dụ

pragma solidity ^0.5.0;

contract Test {
   address public owner = msg.sender;
   uint public creationTime = now;

   modifier onlyBy(address _account) {
      require(
         msg.sender == _account,
         "Sender not authorized."
      );
      _;
   }
   function changeOwner(address _newOwner) public onlyBy(owner) {
      owner = _newOwner;
   }
   modifier onlyAfter(uint _time) {
      require(
         now >= _time,
         "Function called too early."
      );
      _;
   }
   function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
      delete owner;
   }
   modifier costs(uint _amount) {
      require(
         msg.value >= _amount,
         "Not enough Ether provided."
      );
      _;
      if (msg.value > _amount)
         msg.sender.transfer(msg.value - _amount);
   }
   function forceOwnerChange(address _newOwner) public payable costs(200 ether) {
      owner = _newOwner;
      if (uint(owner) & 0 == 1) return;        
   }
}

Hợp đồng trong Solidity tương tự như một Lớp trong C ++. Hợp đồng có các thuộc tính sau.

  • Constructor - Một hàm đặc biệt được khai báo với từ khóa constructor sẽ được thực thi một lần cho mỗi hợp đồng và được gọi khi một hợp đồng được tạo.

  • State Variables - Các biến trên mỗi Hợp đồng để lưu trữ trạng thái của hợp đồng.

  • Functions - Các chức năng trên mỗi Hợp đồng có thể sửa đổi các biến trạng thái để thay đổi trạng thái của hợp đồng.

Bộ định lượng khả năng hiển thị

Sau đây là các định lượng hiển thị khác nhau cho các hàm / biến trạng thái của một hợp đồng.

  • external- Các chức năng bên ngoài có nghĩa là được gọi bằng các hợp đồng khác. Chúng không thể được sử dụng cho cuộc gọi nội bộ. Để gọi hàm bên ngoài trong hợp đồng, cần phải gọi hàm this. Function_name (). Biến trạng thái không thể được đánh dấu là bên ngoài.

  • public- Các hàm / Biến công khai có thể được sử dụng cả bên ngoài và bên trong. Đối với biến trạng thái công khai, Solidity tự động tạo một hàm getter.

  • internal - Các hàm / Biến nội bộ chỉ có thể được sử dụng trong nội bộ hoặc bằng các hợp đồng dẫn xuất.

  • private - Các hàm / Biến riêng chỉ có thể được sử dụng trong nội bộ và thậm chí không được sử dụng bởi các hợp đồng dẫn xuất.

Thí dụ

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//External Contract
contract D {
   function readData() public returns(uint) {
      C c = new C();
      c.updateData(7);         
      return c.getData();
   }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity . Chạy nhiều phương pháp Hợp đồng. Đối với E.getComputedResult () theo sau là E.getResult () hiển thị -

Đầu ra

0: uint256: 8

Kế thừa là một cách để mở rộng chức năng của hợp đồng. Solidity hỗ trợ cả đơn cũng như đa kế thừa. Sau đây là các danh sách cao chính.

  • Một hợp đồng dẫn xuất có thể truy cập tất cả các thành viên không phải private bao gồm các phương thức nội bộ và các biến trạng thái. Nhưng không được phép sử dụng điều này.

  • Ghi đè hàm được cho phép với điều kiện chữ ký hàm vẫn giữ nguyên. Trong trường hợp có sự khác biệt của các tham số đầu ra, quá trình biên dịch sẽ không thành công.

  • Chúng ta có thể gọi hàm siêu hợp đồng bằng cách sử dụng từ khóa super hoặc sử dụng tên siêu hợp đồng.

  • Trong trường hợp đa kế thừa, việc gọi hàm sử dụng super ưu tiên cho hầu hết các hợp đồng dẫn xuất.

Thí dụ

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity . Chạy nhiều phương pháp Hợp đồng. Đối với E.getComputedResult () theo sau là E.getResult () hiển thị -

Đầu ra

0: uint256: 8

Hàm tạo là một hàm đặc biệt được khai báo bằng cách sử dụng constructortừ khóa. Nó là một funtion tùy chọn và được sử dụng để khởi tạo các biến trạng thái của một hợp đồng. Sau đây là các đặc điểm chính của một hàm tạo.

  • Một hợp đồng chỉ có thể có một phương thức khởi tạo.

  • Mã khởi tạo được thực thi một lần khi hợp đồng được tạo và nó được sử dụng để khởi tạo trạng thái hợp đồng.

  • Sau khi mã khởi tạo được thực thi, mã cuối cùng được triển khai vào blockchain. Mã này bao gồm các chức năng công khai và mã có thể truy cập thông qua các chức năng công cộng. Mã cấu tạo hoặc bất kỳ phương thức nội bộ nào chỉ được sử dụng bởi hàm tạo không được bao gồm trong mã cuối cùng.

  • Một phương thức khởi tạo có thể là công khai hoặc nội bộ.

  • Một hàm tạo bên trong đánh dấu hợp đồng là trừu tượng.

  • Trong trường hợp không có phương thức khởi tạo nào được xác định, thì một phương thức khởi tạo mặc định sẽ có trong hợp đồng.

pragma solidity ^0.5.0;

contract Test {
   constructor() public {}
}
  • Trong trường hợp, hợp đồng cơ sở có hàm tạo với các đối số, mỗi hợp đồng dẫn xuất phải chuyển chúng.

  • Hàm tạo cơ sở có thể được khởi tạo trực tiếp bằng cách sau:

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base (5) {
   constructor() public {}
}
  • Hàm tạo cơ sở có thể được khởi tạo gián tiếp bằng cách sau:

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base {
   constructor(uint _info) Base(_info * _info) public {}
}
  • Không được phép sử dụng các cách Trực tiếp và Gián tiếp để khởi tạo trình tạo hợp đồng cơ sở.

  • Nếu hợp đồng dẫn xuất không chuyển (các) đối số đến phương thức tạo hợp đồng cơ sở thì hợp đồng dẫn xuất sẽ trở thành trừu tượng.

Hợp đồng Tóm tắt là một trong đó có ít nhất một chức năng mà không cần thực hiện bất kỳ. Hợp đồng như vậy được sử dụng như một hợp đồng cơ sở. Nói chung, một hợp đồng trừu tượng chứa cả các hàm được thực hiện cũng như các hàm trừu tượng. Hợp đồng có nguồn gốc sẽ thực hiện chức năng trừu tượng và sử dụng các chức năng hiện có khi được yêu cầu.

Trong trường hợp, một hợp đồng dẫn xuất không thực hiện chức năng trừu tượng thì hợp đồng dẫn xuất này sẽ được đánh dấu là trừu tượng.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách các hợp đồng trừu tượng hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract Calculator {
   function getResult() public view returns(uint);
}
contract Test is Calculator {
   function getResult() public view returns(uint) {
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Đầu ra

0: uint256: 3

Các giao diện tương tự như các hợp đồng trừu tượng và được tạo bằng cách sử dụng interfacetừ khóa. Sau đây là các đặc điểm chính của giao diện.

  • Giao diện không thể có bất kỳ chức năng nào với việc triển khai.

  • Các chức năng của một giao diện chỉ có thể thuộc loại bên ngoài.

  • Giao diện không thể có hàm tạo.

  • Giao diện không thể có biến trạng thái.

  • Giao diện có thể có enum, cấu trúc có thể được truy cập bằng cách sử dụng ký hiệu dấu chấm tên giao diện.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách giao diện hoạt động trong Solidity.

pragma solidity ^0.5.0;

interface Calculator {
   function getResult() external view returns(uint);
}
contract Test is Calculator {
   constructor() public {}
   function getResult() external view returns(uint){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Note - Chọn Kiểm tra từ trình đơn thả xuống trước khi nhấp vào nút triển khai.

Đầu ra

0: uint256: 3

Thư viện tương tự như Hợp đồng nhưng chủ yếu nhằm mục đích sử dụng lại. Thư viện chứa các hàm mà các hợp đồng khác có thể gọi. Solidity có những hạn chế nhất định đối với việc sử dụng Thư viện. Sau đây là các đặc điểm chính của Thư viện Solidity.

  • Các chức năng thư viện có thể được gọi trực tiếp nếu chúng không sửa đổi trạng thái. Điều đó có nghĩa là các hàm thuần túy hoặc chỉ xem có thể được gọi từ bên ngoài thư viện.

  • Thư viện không thể bị phá hủy vì nó được cho là không có trạng thái.

  • Thư viện không thể có các biến trạng thái.

  • Thư viện không thể kế thừa bất kỳ phần tử nào.

  • Thư viện không thể được kế thừa.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách hoạt động của Thư viện trong Solidity.

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++) if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;
      
      //search if value is present in the array using Library function
      uint index = Search.indexOf(data, value);
      return index;
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Note - Chọn Kiểm tra từ trình đơn thả xuống trước khi nhấp vào nút triển khai.

Đầu ra

0: uint256: 3

Sử dụng cho

Chỉ thị using A for B; có thể được sử dụng để đính kèm các hàm thư viện của thư viện A với một kiểu nhất định B. Các hàm này sẽ sử dụng kiểu người gọi làm tham số đầu tiên của chúng (được xác định bằng cách sử dụng self).

Thí dụ

Hãy thử đoạn mã sau để hiểu cách hoạt động của Thư viện trong Solidity.

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++)if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   using Search for uint[];
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;      
      
      //Now data is representing the Library
      uint index = data.indexOf(value);
      return index;
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Note - Chọn Kiểm tra từ trình đơn thả xuống trước khi nhấp vào nút triển khai.

Đầu ra

0: uint256: 3

Solidity cung cấp một tùy chọn sử dụng hợp ngữ để viết hợp ngữ nội tuyến trong mã nguồn Solidity. Chúng ta cũng có thể viết một mã lắp ráp độc lập sau đó được chuyển đổi thành mã bytecode. Standalone Assembly là một ngôn ngữ trung gian cho trình biên dịch Solidity và nó chuyển đổi mã Solidity thành một Assembly độc lập và sau đó thành mã byte. Chúng ta có thể sử dụng cùng một ngôn ngữ được sử dụng trong Inline Assembly để viết mã trong một assembly độc lập.

Hội đồng nội tuyến

Mã lắp ráp nội tuyến có thể được xen kẽ trong cơ sở mã Solidity để có khả năng kiểm soát chi tiết hơn đối với EVM và được sử dụng đặc biệt trong khi viết các hàm thư viện.

Mã hợp ngữ được viết dưới assembly { ... } khối.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách hoạt động của Thư viện trong Solidity.

pragma solidity ^0.5.0;

library Sum {   
   function sumUsingInlineAssembly(uint[] memory _data) public pure returns (uint o_sum) {
      for (uint i = 0; i < _data.length; ++i) {
         assembly {
            o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
         }
      }
   }
}
contract Test {
   uint[] data;
   
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function sum() external view returns(uint){      
      return Sum.sumUsingInlineAssembly(data);
   }
}

Chạy chương trình trên bằng các bước được cung cấp trong chương Ứng dụng đầu tiên của Solidity .

Note - Chọn Kiểm tra từ trình đơn thả xuống trước khi nhấp vào nút triển khai.

Đầu ra

0: uint256: 15

Sự kiện là một thành viên có thể kế thừa của một hợp đồng. Một sự kiện được phát ra, nó lưu trữ các đối số được truyền trong nhật ký giao dịch. Các bản ghi này được lưu trữ trên blockchain và có thể truy cập bằng địa chỉ của hợp đồng cho đến khi hợp đồng có mặt trên blockchain. Một sự kiện được tạo ra không thể truy cập được từ bên trong các hợp đồng, thậm chí không phải sự kiện đã tạo và phát ra chúng.

Một sự kiện có thể được khai báo bằng từ khóa sự kiện.

//Declare an Event
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);

//Emit an event
emit Deposit(msg.sender, _id, msg.value);

Thí dụ

Hãy thử đoạn mã sau để hiểu cách một sự kiện hoạt động trong Solidity.

Đầu tiên Tạo một hợp đồng và tạo ra một sự kiện.

pragma solidity ^0.5.0;

contract Test {
   event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
   function deposit(bytes32 _id) public payable {      
      emit Deposit(msg.sender, _id, msg.value);
   }
}

Sau đó, truy cập sự kiện của hợp đồng bằng mã JavaScript.

var abi = /* abi as generated using compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceiptContract = ClientReceipt.at("0x1234...ab67" /* address */);

var event = clientReceiptContract.Deposit(function(error, result) {
   if (!error)console.log(result);
});

Nó sẽ in các chi tiết tương tự như sau:

Đầu ra

{
   "returnValues": {
      "_from": "0x1111...FFFFCCCC",
      "_id": "0x50...sd5adb20",
      "_value": "0x420042"
   },
   "raw": {
      "data": "0x7f...91385",
      "topics": ["0xfd4...b4ead7", "0x7f...1a91385"]
   }
}

Solidity cung cấp nhiều chức năng khác nhau để xử lý lỗi. Nói chung khi xảy ra lỗi, trạng thái được hoàn nguyên trở lại trạng thái ban đầu. Các kiểm tra khác là để ngăn chặn truy cập mã trái phép. Sau đây là một số phương pháp quan trọng được sử dụng để xử lý lỗi:

  • assert(bool condition)- Trong trường hợp điều kiện không được đáp ứng, lệnh gọi phương thức này gây ra opcode không hợp lệ và mọi thay đổi được thực hiện đối với trạng thái đã được hoàn nguyên. Phương pháp này được sử dụng cho các lỗi nội bộ.

  • require(bool condition)- Trong trường hợp điều kiện không được đáp ứng, lệnh gọi phương thức này sẽ trở lại trạng thái ban đầu. - Phương pháp này được sử dụng cho các lỗi đầu vào hoặc các thành phần bên ngoài.

  • require(bool condition, string memory message)- Trong trường hợp điều kiện không được đáp ứng, lệnh gọi phương thức này sẽ trở lại trạng thái ban đầu. - Phương pháp này được sử dụng cho các lỗi đầu vào hoặc các thành phần bên ngoài. Nó cung cấp một tùy chọn để cung cấp một thông điệp tùy chỉnh.

  • revert() - Phương thức này hủy bỏ việc thực thi và hoàn nguyên mọi thay đổi được thực hiện về trạng thái.

  • revert(string memory reason)- Phương thức này hủy bỏ việc thực thi và hoàn nguyên mọi thay đổi được thực hiện về trạng thái. Nó cung cấp một tùy chọn để cung cấp một thông điệp tùy chỉnh.

Thí dụ

Hãy thử đoạn mã sau để hiểu cách xử lý lỗi hoạt động trong Solidity.

pragma solidity ^0.5.0;

contract Vendor {
   address public seller;
   modifier onlySeller() {
      require(
         msg.sender == seller,
         "Only seller can call this."
      );
      _;
   }
   function sell(uint amount) public payable onlySeller { 
      if (amount > msg.value / 2 ether)
         revert("Not enough Ether provided.");
      // Perform the sell operation.
   }
}

Khi hoàn nguyên được gọi, nó sẽ trả về dữ liệu thập lục phân như sau.

Đầu ra

0x08c379a0                     // Function selector for Error(string)
0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset
0x000000000000000000000000000000000000000000000000000000000000001a // String length
0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data