Nhảy đến nội dung chính
Change page

Cấu trúc của các hợp đồng thông minh

Lần cập nhật trang lần cuối: 23 tháng 2, 2026

Hợp đồng thông minh là một chương trình chạy tại một địa chỉ của người dùng trên mạng Ethereum. Chúng được tạo nên bởi dữ liệu và các hàm. Khi nhận được một giao dịch, các hợp đồng thông minh sẽ thực thi. Dưới đây là tổng quan về những gì tạo nên một hợp đồng thông minh.

Điều kiện tiên quyết

Hãy đảm bảo rằng bạn đã đọc về hợp đồng thông minh trước tiên. Tài liệu này giả định rằng bạn đã biết rõ các ngôn ngữ lập trình như JavaScript hoặc Python.

Dữ liệu

Bất kỳ dữ liệu hợp đồng nào cũng phải được gán cho một vị trí: storage hoặc memory. Việc chỉnh sửa nơi lưu trữ trong một hợp đồng thông minh rất tốn kém, vì vậy bạn cần cân nhắc xem dữ liệu của mình sẽ được lưu ở đâu.

Lưu trữ

Storage là nơi lưu trữu các dữ liệu được truy không thường xuyên, ít khi được sửa đổi và được đại diện bởi các biến trạng thái. Các giá trị này được lưu trữ vĩnh viễn trên chuỗi khối. Bạn cần khai báo kiểu dữ liệu để hợp đồng có thể theo dõi được dung lượng lưu trữ trên chuỗi khối mà nó cần khi biên dịch.

1// Ví dụ về Solidity
2contract SimpleStorage {
3 uint storedData; // Biến trạng thái
4 // ...
5}
1# Ví dụ về Vyper
2storedData: int128

Nếu bạn đã từng lập trình hướng đối tượng, bạn có thể sẽ quen với hầu hết các loại ngôn ngữ. Tuy nhiên, address có thể sẽ là một khái niệm mới đối với bạn nếu bạn mới bắt đầu phát triển trên Ethereum.

Một loại address có thể chứa một địa chỉ Ethereum tương đương với 20 byte hoặc 160 bit. Nó trả về một giá trị bắt đầu bằng 0x ở dạng thập lục phân.

Các loại kiểu dữ liệu khác:

  • boolean
  • interger
  • fixed point numbers
  • mảng tĩnh
  • mảng byte có kích thước động
  • giá trị cố định hữu tỉ và số nguyên
  • giá trị cố định chuỗi
  • giá trị cố định thập lục phân
  • enums

Để rõ hơn, hãy tìm hiểu thêm các tài liệu:

Bộ nhớ

Memory là nơi lưu trữ các giá trị chỉ được sử dụng trong khi thực thi một hàm của hợp đồng và được đại diện bởi các biến memory. Vì các giá trị đó không được lưu trữ vĩnh viễn trên chuỗi khối nên chúng không tốn nhiều tài nguyên khi sử dụng.

Tìm hiểu thêm về cách Máy ảo Ethereum (EVM) lưu trữ dữ liệu (Storage, Memory và Stack) trong tài liệu Solidityopens in a new tab.

Các biến môi trường

Bên cạnh những biến mà bạn định nghĩa trong hợp đồng của bạn, có một số biến toàn cục đặc biệt. Chúng được sử dụng chủ yếu để cung cấp thông tin về chuỗi khối hoặc giao dịch hiện tại.

Ví dụ:

Thuộc tínhBiến trạng tháiMô tả
block.timestampuint256Khối epoch timestamp hiện tại
msg.senderđịa chỉNgười gửi thông điệp (cuộc gọi hiện tại)

Các hàm

Nói một cách đơn giản, các hàm được dùng để lấy thông tin hoặc thiết lập thông tin để phản hồi các giao dịch đến.

Có hai loại lời gọi hàm:

  • internal – các hàm này không tạo lệnh gọi EVM
    • Các hàm nội bộ và biến trạng thái chỉ có thể được truy cập nội bộ (tức là từ bên trong hợp đồng hiện tại hoặc các hợp đồng kế thừa từ nó)
  • external – các hàm này tạo lệnh gọi EVM
    • Các hàm external là một phần của giao diện hợp đồng, điều này có nghĩa là các hàm này có thể được gọi từ các hợp đồng khác và qua các giao dịch. Một hàm bên ngoài f không thể được gọi nội bộ (tức là f() không hoạt động, nhưng this.f() thì hoạt động).

Chúng cũng có thể là public hoặc private

  • Các hàm public có thể được gọi nội bộ từ bên trong hợp đồng hoặc bên ngoài thông qua các thông điệp
  • Các hàm private chỉ hiển thị đối với hợp đồng mà chúng được định nghĩa và không hiển thị trong các hợp đồng phái sinh

Tất cả các hàm và các biến trạng thái đều có thể được khởi tạo là public hoặc private

Dưới đây là một hàm có chức năng cập nhật một biến trạng thái cho một hợp đồng:

1// Ví dụ về Solidity
2function update_name(string value) public {
3 dapp_name = value;
4}
  • Tham số value của loại string được chuyển vào hàm: update_name
  • Hàm được khai báo là public, nghĩa là bất kỳ ai cũng có thể truy cập
  • Hàm không được khai báo view nên có thể sửa đổi trạng thái hợp đồng

Các hàm View

Các hàm này không được phép chỉnh sửa trạng thái dữ liệu của hợp đồng. Ví dụ phổ biến là hàm "getter" - bạn có thể sử dụng hàm này để lấy thông tin số dư của người dùng chẳng hạn.

1// Ví dụ về Solidity
2function balanceOf(address _owner) public view returns (uint256 _balance) {
3 return ownerPizzaCount[_owner];
4}
1dappName: public(string)
2
3@view
4@public
5def readName() -> string:
6 return dappName

Những điều được coi là thay đổi trạng thái hợp đồng:

  1. Ghi vào các biến trạng thái.
  2. Phát sự kiệnopens in a new tab.
  3. Tạo các hợp đồng khácopens in a new tab.
  4. Sử dụng selfdestruct.
  5. Gửi Ethers qua các lời gọi.
  6. Gọi bất kỳ hàm nào không được đánh dấu là view hoặc pure.
  7. Sử dụng các lời gọi cấp thấp.
  8. Sử dụng mã assembly nội dòng có chứa các mã vận hành nhất định.

Các hàm constructor

Các hàm constructor chỉ được thực thi một lần khi hợp đồng được triển khai lần đầu tiên. Giống như constructor trong nhiều ngôn ngữ lập trình dựa trên lớp, các hàm này thường khởi tạo các biến trạng thái theo các giá trị được chỉ định của chúng.

1// Ví dụ về Solidity
2// Khởi tạo dữ liệu của hợp đồng, đặt `owner`
3// thành địa chỉ của người tạo hợp đồng.
4constructor() public {
5 // Tất cả các hợp đồng thông minh đều dựa vào các giao dịch bên ngoài để kích hoạt các hàm của chúng.
6 // `msg` là một biến toàn cục bao gồm dữ liệu liên quan về giao dịch nhất định,
7 // chẳng hạn như địa chỉ của người gửi và giá trị ETH có trong giao dịch.
8 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
9 owner = msg.sender;
10}
Hiện tất cả
1# Ví dụ về Vyper
2
3@external
4def __init__(_beneficiary: address, _bidding_time: uint256):
5 self.beneficiary = _beneficiary
6 self.auctionStart = block.timestamp
7 self.auctionEnd = self.auctionStart + _bidding_time

Các hàm dựng sẵn

Ngoài các biến và các hàm bạn định nghĩa trong hợp đồng của bạn, còn có một số hàm tích hợp đặc biệt. Ví dụ rõ ràng nhất là:

  • address.send() – Solidity
  • send(address) – Vyper

Hai hàm trên cho phép những hợp đồng gửi ETH đến các tài khoản khác.

Viết hàm

Hàm của bạn cần:

  • biến tham số và kiểu dữ liệu của nó (nếu hàm đó cho phép truyền các tham số)
  • khai báo internal/external
  • khai báo pure/view/payable
  • kiểu dữ liệu trả về (nếu hàm đó trả về một giá trị)
1pragma solidity >=0.4.0 <=0.6.0;
2
3contract ExampleDapp {
4 string dapp_name; // biến trạng thái
5
6 // Được gọi khi hợp đồng được triển khai và khởi tạo giá trị
7 constructor() public {
8 dapp_name = "Ứng dụng phi tập trung ví dụ của tôi";
9 }
10
11 // Hàm Get
12 function read_name() public view returns(string) {
13 return dapp_name;
14 }
15
16 // Hàm Set
17 function update_name(string value) public {
18 dapp_name = value;
19 }
20}
Hiện tất cả

Một hợp đồng hoàn chỉnh có thể trông giống như trên. Ở đây, hàm constructor cung cấp một giá trị ban đầu cho biến dapp_name.

Các sự kiện và nhật ký

Sự kiện cho phép hợp đồng thông minh của bạn giao tiếp với giao diện người dùng hoặc các ứng dụng đăng ký khác. Khi một giao dịch được xác thực và thêm vào một khối, các hợp đồng thông minh có thể phát ra tín hiệu và ghi lại thông tin, điều này sẽ được phần mềm phía trước xử lý và sử dụng.

Các ví dụ có chú thích

Dưới đây là một vài ví dụ được viết bằng Solidity. Nếu bạn muốn thử nghiệm với mã, bạn có thể tương tác với chúng trong Remixopens in a new tab.

Xin chào thế giới

1// Chỉ định phiên bản của Solidity, sử dụng lập phiên bản ngữ nghĩa.
2// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity ^0.5.10;
4
5// Định nghĩa một hợp đồng có tên là `HelloWorld`.
6// Hợp đồng là một tập hợp các hàm và dữ liệu (trạng thái của nó).
7// Sau khi được triển khai, một hợp đồng sẽ nằm tại một địa chỉ cụ thể trên chuỗi khối Ethereum.
8// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // Khai báo một biến trạng thái `message` thuộc loại `string`.
12 // Các biến trạng thái là các biến có giá trị được lưu trữ vĩnh viễn trong bộ lưu trữ hợp đồng.
13 // Từ khóa `public` giúp các biến có thể truy cập được từ bên ngoài hợp đồng
14 // và tạo một hàm mà các hợp đồng hoặc ứng dụng khách khác có thể gọi để truy cập giá trị.
15 string public message;
16
17 // Tương tự như nhiều ngôn ngữ lập trình hướng đối tượng dựa trên lớp, một hàm constructor là
18 // một hàm đặc biệt chỉ được thực thi khi tạo hợp đồng.
19 // Các hàm constructor được sử dụng để khởi tạo dữ liệu của hợp đồng.
20 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) public {
22 // Chấp nhận một đối số chuỗi `initMessage` và đặt giá trị
23 // vào biến lưu trữ `message` của hợp đồng).
24 message = initMessage;
25 }
26
27 // Một hàm public chấp nhận một đối số chuỗi
28 // và cập nhật biến lưu trữ `message`.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Hiện tất cả

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Một `address` có thể so sánh với một địa chỉ email - nó được dùng để xác định một tài khoản trên Ethereum.
5 // Các địa chỉ có thể đại diện cho một hợp đồng thông minh hoặc tài khoản bên ngoài (người dùng).
6 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Một `mapping` về cơ bản là một cấu trúc dữ liệu bảng băm.
10 // `mapping` này gán một số nguyên không dấu (số dư token) cho một địa chỉ (người nắm giữ token).
11 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapping (address => uint) public balances;
13
14 // Các sự kiện cho phép ghi lại hoạt động trên chuỗi khối.
15 // Các ứng dụng khách Ethereum có thể lắng nghe các sự kiện để phản ứng với các thay đổi trạng thái hợp đồng.
16 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events
17 event Transfer(address from, address to, uint amount);
18
19 // Khởi tạo dữ liệu của hợp đồng, đặt `owner`
20 // thành địa chỉ của người tạo hợp đồng.
21 constructor() public {
22 // Tất cả các hợp đồng thông minh đều dựa vào các giao dịch bên ngoài để kích hoạt các hàm của chúng.
23 // `msg` là một biến toàn cục bao gồm dữ liệu liên quan về giao dịch nhất định,
24 // chẳng hạn như địa chỉ của người gửi và giá trị ETH có trong giao dịch.
25 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
26 owner = msg.sender;
27 }
28
29 // Tạo một lượng token mới và gửi chúng đến một địa chỉ.
30 function mint(address receiver, uint amount) public {
31 // `require` là một cấu trúc điều khiển được sử dụng để thực thi các điều kiện nhất định.
32 // Nếu câu lệnh `require` ước tính là `false`, một ngoại lệ sẽ được kích hoạt,
33 // hoàn nguyên tất cả các thay đổi được thực hiện đối với trạng thái trong lệnh gọi hiện tại.
34 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
35
36 // Chỉ chủ sở hữu hợp đồng mới có thể gọi hàm này
37 require(msg.sender == owner, "Bạn không phải là chủ sở hữu.");
38
39 // Thực thi số lượng token tối đa
40 require(amount < 1e60, "Vượt quá mức phát hành tối đa");
41
42 // Tăng số dư của `receiver` lên `amount`
43 balances[receiver] += amount;
44 }
45
46 // Gửi một lượng token hiện có từ bất kỳ người gọi nào đến một địa chỉ.
47 function transfer(address receiver, uint amount) public {
48 // Người gửi phải có đủ token để gửi
49 require(amount <= balances[msg.sender], "Số dư không đủ.");
50
51 // Điều chỉnh số dư token của hai địa chỉ
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Phát sự kiện được xác định trước đó
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Hiện tất cả

Tài sản kỹ thuật số duy nhất

1pragma solidity ^0.5.10;
2
3// Nhập các ký hiệu từ các tệp khác vào hợp đồng hiện tại.
4// Trong trường hợp này, một loạt các hợp đồng trợ giúp từ OpenZeppelin.
5// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files
6
7import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";
8import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
9import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";
10import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
11
12// Từ khóa `is` được sử dụng để kế thừa các hàm và từ khóa từ các hợp đồng bên ngoài.
13// Trong trường hợp này, `CryptoPizza` kế thừa từ các hợp đồng `IERC721` và `ERC165`.
14// Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
15contract CryptoPizza is IERC721, ERC165 {
16 // Sử dụng thư viện SafeMath của OpenZeppelin để thực hiện các phép toán số học một cách an toàn.
17 // Tìm hiểu thêm: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath
18 using SafeMath for uint256;
19
20 // Các biến trạng thái không đổi trong Solidity tương tự như các ngôn ngữ khác
21 // nhưng bạn phải gán từ một biểu thức không đổi tại thời điểm biên dịch.
22 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables
23 uint256 constant dnaDigits = 10;
24 uint256 constant dnaModulus = 10 ** dnaDigits;
25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
26
27 // Các loại cấu trúc cho phép bạn xác định loại của riêng mình
28 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
29 struct Pizza {
30 string name;
31 uint256 dna;
32 }
33
34 // Tạo một mảng trống gồm các cấu trúc Pizza
35 Pizza[] public pizzas;
36
37 // Ánh xạ từ ID pizza đến địa chỉ của chủ sở hữu
38 mapping(uint256 => address) public pizzaToOwner;
39
40 // Ánh xạ từ địa chỉ của chủ sở hữu đến số lượng token sở hữu
41 mapping(address => uint256) public ownerPizzaCount;
42
43 // Ánh xạ từ ID token đến địa chỉ được phê duyệt
44 mapping(uint256 => address) pizzaApprovals;
45
46 // Bạn có thể lồng các ánh xạ, ví dụ này ánh xạ chủ sở hữu với các phê duyệt của nhà điều hành
47 mapping(address => mapping(address => bool)) private operatorApprovals;
48
49 // Hàm nội bộ để tạo một Pizza ngẫu nhiên từ chuỗi (tên) và DNA
50 function _createPizza(string memory _name, uint256 _dna)
51 // Từ khóa `internal` có nghĩa là hàm này chỉ hiển thị
52 // trong hợp đồng này và các hợp đồng kế thừa từ hợp đồng này
53 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
54 internal
55 // `isUnique` là một bộ điều chỉnh hàm kiểm tra xem pizza đã tồn tại chưa
56 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
57 isUnique(_name, _dna)
58 {
59 // Thêm Pizza vào mảng Pizza và lấy id
60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
61
62 // Kiểm tra xem chủ sở hữu Pizza có giống với người dùng hiện tại không
63 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
64
65 // lưu ý rằng address(0) là địa chỉ không,
66 // cho biết rằng pizza[id] chưa được phân bổ cho một người dùng cụ thể.
67
68 assert(pizzaToOwner[id] == address(0));
69
70 // Ánh xạ Pizza cho chủ sở hữu
71 pizzaToOwner[id] = msg.sender;
72 ownerPizzaCount[msg.sender] = SafeMath.add(
73 ownerPizzaCount[msg.sender],
74 1
75 );
76 }
77
78 // Tạo một Pizza ngẫu nhiên từ chuỗi (tên)
79 function createRandomPizza(string memory _name) public {
80 uint256 randDna = generateRandomDna(_name, msg.sender);
81 _createPizza(_name, randDna);
82 }
83
84 // Tạo DNA ngẫu nhiên từ chuỗi (tên) và địa chỉ của chủ sở hữu (người tạo)
85 function generateRandomDna(string memory _str, address _owner)
86 public
87 // Các hàm được đánh dấu là `pure` cam kết không đọc hoặc sửa đổi trạng thái
88 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
89 pure
90 returns (uint256)
91 {
92 // Tạo uint ngẫu nhiên từ chuỗi (tên) + địa chỉ (chủ sở hữu)
93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
94 uint256(_owner);
95 rand = rand % dnaModulus;
96 return rand;
97 }
98
99 // Trả về mảng Pizza được tìm thấy bởi chủ sở hữu
100 function getPizzasByOwner(address _owner)
101 public
102 // Các hàm được đánh dấu là `view` cam kết không sửa đổi trạng thái
103 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
104 view
105 returns (uint256[] memory)
106 {
107 // Sử dụng vị trí lưu trữ `memory` để lưu trữ các giá trị chỉ trong
108 // vòng đời của lệnh gọi hàm này.
109 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack
110 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);
111 uint256 counter = 0;
112 for (uint256 i = 0; i < pizzas.length; i++) {
113 if (pizzaToOwner[i] == _owner) {
114 result[counter] = i;
115 counter++;
116 }
117 }
118 return result;
119 }
120
121 // Chuyển Pizza và quyền sở hữu cho địa chỉ khác
122 function transferFrom(address _from, address _to, uint256 _pizzaId) public {
123 require(_from != address(0) && _to != address(0), "Địa chỉ không hợp lệ.");
124 require(_exists(_pizzaId), "Pizza không tồn tại.");
125 require(_from != _to, "Không thể chuyển đến cùng một địa chỉ.");
126 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Địa chỉ không được phê duyệt.");
127
128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
130 pizzaToOwner[_pizzaId] = _to;
131
132 // Phát sự kiện được xác định trong hợp đồng IERC721 đã nhập
133 emit Transfer(_from, _to, _pizzaId);
134 _clearApproval(_to, _pizzaId);
135 }
136
137 /**
138 * Chuyển quyền sở hữu của một ID token nhất định sang một địa chỉ khác một cách an toàn
139 * Nếu địa chỉ mục tiêu là một hợp đồng, nó phải triển khai `onERC721Received`,
140 * được gọi khi thực hiện một lần chuyển an toàn và trả về giá trị đặc biệt
141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
142 * nếu không, việc chuyển khoản sẽ bị hoàn nguyên.
143 */
144 function safeTransferFrom(address from, address to, uint256 pizzaId)
145 public
146 {
147 // solium-disable-next-line arg-overflow
148 this.safeTransferFrom(from, to, pizzaId, "");
149 }
150
151 /**
152 * Chuyển quyền sở hữu của một ID token nhất định sang một địa chỉ khác một cách an toàn
153 * Nếu địa chỉ mục tiêu là một hợp đồng, nó phải triển khai `onERC721Received`,
154 * được gọi khi thực hiện một lần chuyển an toàn và trả về giá trị đặc biệt
155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
156 * nếu không, việc chuyển khoản sẽ bị hoàn nguyên.
157 */
158 function safeTransferFrom(
159 address from,
160 address to,
161 uint256 pizzaId,
162 bytes memory _data
163 ) public {
164 this.transferFrom(from, to, pizzaId);
165 require(_checkOnERC721Received(from, to, pizzaId, _data), "Phải triển khai onERC721Received.");
166 }
167
168 /**
169 * Hàm nội bộ để gọi `onERC721Received` trên một địa chỉ mục tiêu
170 * Lệnh gọi không được thực thi nếu địa chỉ mục tiêu không phải là hợp đồng
171 */
172 function _checkOnERC721Received(
173 address from,
174 address to,
175 uint256 pizzaId,
176 bytes memory _data
177 ) internal returns (bool) {
178 if (!isContract(to)) {
179 return true;
180 }
181
182 bytes4 retval = IERC721Receiver(to).onERC721Received(
183 msg.sender,
184 from,
185 pizzaId,
186 _data
187 );
188 return (retval == _ERC721_RECEIVED);
189 }
190
191 // Đốt một Pizza - phá hủy hoàn toàn Token
192 // Bộ điều chỉnh hàm `external` có nghĩa là hàm này là
193 // một phần của giao diện hợp đồng và các hợp đồng khác có thể gọi nó
194 function burn(uint256 _pizzaId) external {
195 require(msg.sender != address(0), "Địa chỉ không hợp lệ.");
196 require(_exists(_pizzaId), "Pizza không tồn tại.");
197 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Địa chỉ không được phê duyệt.");
198
199 ownerPizzaCount[msg.sender] = SafeMath.sub(
200 ownerPizzaCount[msg.sender],
201 1
202 );
203 pizzaToOwner[_pizzaId] = address(0);
204 }
205
206 // Trả về số lượng Pizza theo địa chỉ
207 function balanceOf(address _owner) public view returns (uint256 _balance) {
208 return ownerPizzaCount[_owner];
209 }
210
211 // Trả về chủ sở hữu của Pizza được tìm thấy bằng id
212 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
213 address owner = pizzaToOwner[_pizzaId];
214 require(owner != address(0), "ID Pizza không hợp lệ.");
215 return owner;
216 }
217
218 // Phê duyệt địa chỉ khác để chuyển quyền sở hữu Pizza
219 function approve(address _to, uint256 _pizzaId) public {
220 require(msg.sender == pizzaToOwner[_pizzaId], "Phải là chủ sở hữu Pizza.");
221 pizzaApprovals[_pizzaId] = _to;
222 emit Approval(msg.sender, _to, _pizzaId);
223 }
224
225 // Trả về địa chỉ được phê duyệt cho Pizza cụ thể
226 function getApproved(uint256 _pizzaId)
227 public
228 view
229 returns (address operator)
230 {
231 require(_exists(_pizzaId), "Pizza không tồn tại.");
232 return pizzaApprovals[_pizzaId];
233 }
234
235 /**
236 * Hàm riêng tư để xóa phê duyệt hiện tại của một ID token nhất định
237 * Hoàn nguyên nếu địa chỉ đã cho thực sự không phải là chủ sở hữu của token
238 */
239 function _clearApproval(address owner, uint256 _pizzaId) private {
240 require(pizzaToOwner[_pizzaId] == owner, "Phải là chủ sở hữu pizza.");
241 require(_exists(_pizzaId), "Pizza không tồn tại.");
242 if (pizzaApprovals[_pizzaId] != address(0)) {
243 pizzaApprovals[_pizzaId] = address(0);
244 }
245 }
246
247 /*
248 * Đặt hoặc bỏ đặt phê duyệt của một nhà điều hành nhất định
249 * Một nhà điều hành được phép chuyển tất cả các token của người gửi thay mặt họ
250 */
251 function setApprovalForAll(address to, bool approved) public {
252 require(to != msg.sender, "Không thể phê duyệt địa chỉ của chính mình");
253 operatorApprovals[msg.sender][to] = approved;
254 emit ApprovalForAll(msg.sender, to, approved);
255 }
256
257 // Cho biết một nhà điều hành có được một chủ sở hữu nhất định phê duyệt hay không
258 function isApprovedForAll(address owner, address operator)
259 public
260 view
261 returns (bool)
262 {
263 return operatorApprovals[owner][operator];
264 }
265
266 // Nhận quyền sở hữu Pizza - chỉ dành cho người dùng được phê duyệt
267 function takeOwnership(uint256 _pizzaId) public {
268 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Địa chỉ không được phê duyệt.");
269 address owner = this.ownerOf(_pizzaId);
270 this.transferFrom(owner, msg.sender, _pizzaId);
271 }
272
273 // Kiểm tra xem Pizza có tồn tại không
274 function _exists(uint256 pizzaId) internal view returns (bool) {
275 address owner = pizzaToOwner[pizzaId];
276 return owner != address(0);
277 }
278
279 // Kiểm tra xem địa chỉ có phải là chủ sở hữu hoặc được phê duyệt để chuyển Pizza không
280 function _isApprovedOrOwner(address spender, uint256 pizzaId)
281 internal
282 view
283 returns (bool)
284 {
285 address owner = pizzaToOwner[pizzaId];
286 // Disable solium check because of
287 // https://github.com/duaraghav8/Solium/issues/175
288 // solium-disable-next-line operator-whitespace
289 return (spender == owner ||
290 this.getApproved(pizzaId) == spender ||
291 this.isApprovedForAll(owner, spender));
292 }
293
294 // Kiểm tra xem Pizza có phải là duy nhất và chưa tồn tại không
295 modifier isUnique(string memory _name, uint256 _dna) {
296 bool result = true;
297 for (uint256 i = 0; i < pizzas.length; i++) {
298 if (
299 keccak256(abi.encodePacked(pizzas[i].name)) ==
300 keccak256(abi.encodePacked(_name)) &&
301 pizzas[i].dna == _dna
302 ) {
303 result = false;
304 }
305 }
306 require(result, "Pizza với tên như vậy đã tồn tại.");
307 _;
308 }
309
310 // Trả về việc địa chỉ mục tiêu có phải là một hợp đồng không
311 function isContract(address account) internal view returns (bool) {
312 uint256 size;
313 // Hiện tại không có cách nào tốt hơn để kiểm tra xem có hợp đồng trong một địa chỉ hay không
314 // ngoài việc kiểm tra kích thước của mã tại địa chỉ đó.
315 // Xem https://ethereum.stackexchange.com/a/14016/36603
316 // để biết thêm chi tiết về cách hoạt động của nó.
317 // TODO Kiểm tra lại điều này trước khi phát hành Serenity, bởi vì tất cả các địa chỉ sau đó sẽ là
318 // hợp đồng.
319 // solium-disable-next-line security/no-inline-assembly
320 assembly {
321 size := extcodesize(account)
322 }
323 return size > 0;
324 }
325}
Hiện tất cả

Đọc thêm

Xem thêm tài liệu của Solidity và Vyper để có cái nhìn tổng quát hơn về các hợp đồng thông minh:

Bài viết này hữu ích không?