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ề Solidity2contract SimpleStorage {3 uint storedData; // Biến trạng thái4 // ...5}1# Ví dụ về Vyper2storedData: int128Nế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ính | Biến trạng thái | Mô tả |
|---|---|---|
block.timestamp | uint256 | Khố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
fkhông thể được gọi nội bộ (tức làf()không hoạt động, nhưngthis.f()thì hoạt động).
- 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
Chúng cũng có thể là public hoặc private
- Các hàm
publiccó 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
privatechỉ 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ề Solidity2function update_name(string value) public {3 dapp_name = value;4}- Tham số
valuecủa loạistringđượ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
viewnê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ề Solidity2function balanceOf(address _owner) public view returns (uint256 _balance) {3 return ownerPizzaCount[_owner];4}1dappName: public(string)23@view4@public5def readName() -> string:6 return dappNameNhững điều được coi là thay đổi trạng thái hợp đồng:
- Ghi vào các biến trạng thái.
- Phát sự kiệnopens in a new tab.
- Tạo các hợp đồng khácopens in a new tab.
- Sử dụng
selfdestruct. - Gửi Ethers qua các lời gọi.
- Gọi bất kỳ hàm nào không được đánh dấu là
viewhoặcpure. - Sử dụng các lời gọi cấp thấp.
- 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ề Solidity2// 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-properties9 owner = msg.sender;10}Hiện tất cả1# Ví dụ về Vyper23@external4def __init__(_beneficiary: address, _bidding_time: uint256):5 self.beneficiary = _beneficiary6 self.auctionStart = block.timestamp7 self.auctionEnd = self.auctionStart + _bidding_timeCá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()– Soliditysend(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;23contract ExampleDapp {4 string dapp_name; // biến trạng thái56 // Đượ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 }1011 // Hàm Get12 function read_name() public view returns(string) {13 return dapp_name;14 }1516 // Hàm Set17 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#pragma3pragma solidity ^0.5.10;45// Đị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.html9contract HelloWorld {1011 // 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 đồng14 // 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;1617 // 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#constructors21 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 }2627 // Một hàm public chấp nhận một đối số chuỗi28 // 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;23contract 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#address7 address public owner;89 // 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-types12 mapping (address => uint) public balances;1314 // 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#events17 event Transfer(address from, address to, uint amount);1819 // 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-properties26 owner = msg.sender;27 }2829 // 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-exceptions3536 // Chỉ chủ sở hữu hợp đồng mới có thể gọi hàm này37 require(msg.sender == owner, "Bạn không phải là chủ sở hữu.");3839 // Thực thi số lượng token tối đa40 require(amount < 1e60, "Vượt quá mức phát hành tối đa");4142 // Tăng số dư của `receiver` lên `amount`43 balances[receiver] += amount;44 }4546 // 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ửi49 require(amount <= balances[msg.sender], "Số dư không đủ.");5051 // Điều chỉnh số dư token của hai địa chỉ52 balances[msg.sender] -= amount;53 balances[receiver] += amount;5455 // 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;23// 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-files67import "../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";1112// 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#inheritance15contract 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#SafeMath18 using SafeMath for uint256;1920 // Các biến trạng thái không đổi trong Solidity tương tự như các ngôn ngữ khác21 // 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-variables23 uint256 constant dnaDigits = 10;24 uint256 constant dnaModulus = 10 ** dnaDigits;25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;2627 // Các loại cấu trúc cho phép bạn xác định loại của riêng mình28 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs29 struct Pizza {30 string name;31 uint256 dna;32 }3334 // Tạo một mảng trống gồm các cấu trúc Pizza35 Pizza[] public pizzas;3637 // Ánh xạ từ ID pizza đến địa chỉ của chủ sở hữu38 mapping(uint256 => address) public pizzaToOwner;3940 // Ánh xạ từ địa chỉ của chủ sở hữu đến số lượng token sở hữu41 mapping(address => uint256) public ownerPizzaCount;4243 // Ánh xạ từ ID token đến địa chỉ được phê duyệt44 mapping(uint256 => address) pizzaApprovals;4546 // 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ành47 mapping(address => mapping(address => bool)) private operatorApprovals;4849 // Hàm nội bộ để tạo một Pizza ngẫu nhiên từ chuỗi (tên) và DNA50 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ày53 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters54 internal55 // `isUnique` là một bộ điều chỉnh hàm kiểm tra xem pizza đã tồn tại chưa56 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers57 isUnique(_name, _dna)58 {59 // Thêm Pizza vào mảng Pizza và lấy id60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);6162 // Kiểm tra xem chủ sở hữu Pizza có giống với người dùng hiện tại không63 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions6465 // 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ể.6768 assert(pizzaToOwner[id] == address(0));6970 // Ánh xạ Pizza cho chủ sở hữu71 pizzaToOwner[id] = msg.sender;72 ownerPizzaCount[msg.sender] = SafeMath.add(73 ownerPizzaCount[msg.sender],74 175 );76 }7778 // 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 }8384 // 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 public87 // Các hàm được đánh dấu là `pure` cam kết không đọc hoặc sửa đổi trạng thái88 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions89 pure90 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 }9899 // Trả về mảng Pizza được tìm thấy bởi chủ sở hữu100 function getPizzasByOwner(address _owner)101 public102 // Các hàm được đánh dấu là `view` cam kết không sửa đổi trạng thái103 // Tìm hiểu thêm: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions104 view105 returns (uint256[] memory)106 {107 // Sử dụng vị trí lưu trữ `memory` để lưu trữ các giá trị chỉ trong108 // 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-stack110 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 }120121 // Chuyển Pizza và quyền sở hữu cho địa chỉ khác122 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.");127128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);130 pizzaToOwner[_pizzaId] = _to;131132 // Phát sự kiện được xác định trong hợp đồng IERC721 đã nhập133 emit Transfer(_from, _to, _pizzaId);134 _clearApproval(_to, _pizzaId);135 }136137 /**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àn139 * 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ệt141 * `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 public146 {147 // solium-disable-next-line arg-overflow148 this.safeTransferFrom(from, to, pizzaId, "");149 }150151 /**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àn153 * 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ệt155 * `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 _data163 ) public {164 this.transferFrom(from, to, pizzaId);165 require(_checkOnERC721Received(from, to, pizzaId, _data), "Phải triển khai onERC721Received.");166 }167168 /**169 * Hàm nội bộ để gọi `onERC721Received` trên một địa chỉ mục tiêu170 * 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 đồng171 */172 function _checkOnERC721Received(173 address from,174 address to,175 uint256 pizzaId,176 bytes memory _data177 ) internal returns (bool) {178 if (!isContract(to)) {179 return true;180 }181182 bytes4 retval = IERC721Receiver(to).onERC721Received(183 msg.sender,184 from,185 pizzaId,186 _data187 );188 return (retval == _ERC721_RECEIVED);189 }190191 // Đốt một Pizza - phá hủy hoàn toàn Token192 // 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.");198199 ownerPizzaCount[msg.sender] = SafeMath.sub(200 ownerPizzaCount[msg.sender],201 1202 );203 pizzaToOwner[_pizzaId] = address(0);204 }205206 // Trả về số lượng Pizza theo địa chỉ207 function balanceOf(address _owner) public view returns (uint256 _balance) {208 return ownerPizzaCount[_owner];209 }210211 // Trả về chủ sở hữu của Pizza được tìm thấy bằng id212 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 }217218 // Phê duyệt địa chỉ khác để chuyển quyền sở hữu Pizza219 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 }224225 // Trả về địa chỉ được phê duyệt cho Pizza cụ thể226 function getApproved(uint256 _pizzaId)227 public228 view229 returns (address operator)230 {231 require(_exists(_pizzaId), "Pizza không tồn tại.");232 return pizzaApprovals[_pizzaId];233 }234235 /**236 * Hàm riêng tư để xóa phê duyệt hiện tại của một ID token nhất định237 * Hoàn nguyên nếu địa chỉ đã cho thực sự không phải là chủ sở hữu của token238 */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 }246247 /*248 * Đặt hoặc bỏ đặt phê duyệt của một nhà điều hành nhất định249 * 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 }256257 // 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ông258 function isApprovedForAll(address owner, address operator)259 public260 view261 returns (bool)262 {263 return operatorApprovals[owner][operator];264 }265266 // Nhận quyền sở hữu Pizza - chỉ dành cho người dùng được phê duyệt267 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 }272273 // Kiểm tra xem Pizza có tồn tại không274 function _exists(uint256 pizzaId) internal view returns (bool) {275 address owner = pizzaToOwner[pizzaId];276 return owner != address(0);277 }278279 // Kiểm tra xem địa chỉ có phải là chủ sở hữu hoặc được phê duyệt để chuyển Pizza không280 function _isApprovedOrOwner(address spender, uint256 pizzaId)281 internal282 view283 returns (bool)284 {285 address owner = pizzaToOwner[pizzaId];286 // Disable solium check because of287 // https://github.com/duaraghav8/Solium/issues/175288 // solium-disable-next-line operator-whitespace289 return (spender == owner ||290 this.getApproved(pizzaId) == spender ||291 this.isApprovedForAll(owner, spender));292 }293294 // Kiểm tra xem Pizza có phải là duy nhất và chưa tồn tại không295 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 == _dna302 ) {303 result = false;304 }305 }306 require(result, "Pizza với tên như vậy đã tồn tại.");307 _;308 }309310 // Trả về việc địa chỉ mục tiêu có phải là một hợp đồng không311 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ông314 // 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/36603316 // để 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-assembly320 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:
Các chủ đề liên quan
Các hướng dẫn liên quan
- Thu nhỏ hợp đồng để chống lại giới hạn kích thước hợp đồng – Một số mẹo thực tế để giảm kích thước hợp đồng thông minh của bạn.
- Ghi dữ liệu từ các hợp đồng thông minh bằng các sự kiện – Giới thiệu về các sự kiện hợp đồng thông minh và cách bạn có thể sử dụng chúng để ghi dữ liệu.
- Tương tác với các hợp đồng khác từ Solidity – Cách triển khai một hợp đồng thông minh từ một hợp đồng hiện có và tương tác với nó.