Tương tác với các hợp đồng khác từ Solidity
Trong các hướng dẫn trước, chúng ta đã học được rất nhiều về cách triển khai hợp đồng thông minh đầu tiên của bạn và thêm một số tính năng cho nó như kiểm soát quyền truy cập bằng các bộ sửa đổi (opens in a new tab) hoặc xử lý lỗi trong Solidity (opens in a new tab). Trong hướng dẫn này, chúng ta sẽ học 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ó.
Chúng ta sẽ tạo một hợp đồng cho phép bất kỳ ai có hợp đồng thông minh Counter của riêng họ bằng cách tạo một factory cho nó, tên của nó sẽ là CounterFactory. Đầu tiên đây là mã của hợp đồng thông minh Counter ban đầu của chúng ta:
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "Bạn không phải là chủ sở hữu của hợp đồng");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Bạn cần sử dụng factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}Hiện tất cảLưu ý rằng chúng tôi đã sửa đổi một chút mã hợp đồng để theo dõi địa chỉ của factory và địa chỉ của chủ sở hữu hợp đồng. Khi bạn gọi mã hợp đồng từ một hợp đồng khác, msg.sender sẽ tham chiếu đến địa chỉ của factory hợp đồng của chúng ta. Đây là một điểm thực sự quan trọng cần hiểu vì việc sử dụng một hợp đồng để tương tác với các hợp đồng khác là một thông lệ phổ biến. Do đó, bạn nên cẩn thận về việc ai là người gửi trong các trường hợp phức tạp.
Vì vậy, chúng tôi cũng đã thêm một bộ sửa đổi onlyFactory để đảm bảo rằng hàm thay đổi trạng thái chỉ có thể được gọi bởi factory, nơi sẽ chuyển người gọi ban đầu dưới dạng một tham số.
Bên trong CounterFactory mới của chúng ta, sẽ quản lý tất cả các Counter khác, chúng ta sẽ thêm một ánh xạ sẽ liên kết một chủ sở hữu với địa chỉ của hợp đồng counter của họ:
1mapping(address => Counter) _counters;Trong Ethereum, ánh xạ tương đương với các đối tượng trong javascript, chúng cho phép ánh xạ một khóa loại A tới một giá trị loại B. Trong trường hợp này, chúng ta ánh xạ địa chỉ của một chủ sở hữu với phiên bản Counter của nó.
Việc khởi tạo một Counter mới cho ai đó sẽ trông như thế này:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Đầu tiên chúng ta kiểm tra xem người đó đã sở hữu một counter hay chưa. Nếu họ chưa sở hữu một counter, chúng ta sẽ khởi tạo một counter mới bằng cách chuyển địa chỉ của họ cho hàm khởi tạo Counter và gán phiên bản mới được tạo cho ánh xạ.
Để lấy số đếm của một Counter cụ thể, nó sẽ trông như thế này:
1function getCount(address account) public view returns (uint256) {2 require (_counters[account] != Counter(0));3 return (_counters[account].getCount());4}56function getMyCount() public view returns (uint256) {7 return (getCount(msg.sender));8}Hàm đầu tiên kiểm tra xem hợp đồng Counter có tồn tại cho một địa chỉ nhất định hay không và sau đó gọi phương thức getCount từ phiên bản đó. Hàm thứ hai: getMyCount chỉ là một lối tắt để chuyển trực tiếp msg.sender đến hàm getCount.
Hàm increment khá tương tự nhưng chuyển người gửi giao dịch ban đầu đến hợp đồng Counter:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }Lưu ý rằng nếu được gọi quá nhiều lần, bộ đếm của chúng ta có thể bị lỗi tràn số. Bạn nên sử dụng thư viện SafeMath (opens in a new tab) càng nhiều càng tốt để bảo vệ khỏi trường hợp có thể xảy ra này.
Để triển khai hợp đồng của chúng ta, bạn sẽ cần cung cấp cả mã của CounterFactory và Counter. Ví dụ: khi triển khai trong Remix, bạn sẽ cần phải chọn CounterFactory.
Đây là mã đầy đủ:
1pragma solidity 0.5.17;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "Bạn không phải là chủ sở hữu của hợp đồng");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "Bạn cần sử dụng factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}3435contract CounterFactory {3637 mapping(address => Counter) _counters;3839 function createCounter() public {40 require (_counters[msg.sender] == Counter(0));41 _counters[msg.sender] = new Counter(msg.sender);42 }4344 function increment() public {45 require (_counters[msg.sender] != Counter(0));46 Counter(_counters[msg.sender]).increment(msg.sender);47 }4849 function getCount(address account) public view returns (uint256) {50 require (_counters[account] != Counter(0));51 return (_counters[account].getCount());52 }5354 function getMyCount() public view returns (uint256) {55 return (getCount(msg.sender));56 }5758}Hiện tất cảSau khi biên dịch, trong phần triển khai của Remix, bạn sẽ chọn factory để triển khai:
Sau đó, bạn có thể thử nghiệm với factory hợp đồng của mình và kiểm tra sự thay đổi giá trị. Nếu bạn muốn gọi hợp đồng thông minh từ một địa chỉ khác, bạn sẽ cần thay đổi địa chỉ trong phần chọn Tài khoản của Remix.
Lần cập nhật trang lần cuối: 15 tháng 8, 2023
