> For the complete documentation index, see [llms.txt](https://archive-vn.docs.klaytn.foundation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://archive-vn.docs.klaytn.foundation/content/dapp/tutorials/klaystagram/4.-write-klaystagram-smart-contract.md).

# 4. Soạn hợp đồng thông minh Klaystagram

1. Background
2. Contract setup
3. Set events and data structure
4. Write functions\
   4.1. `uploadPhoto`\
   4.2. `transferOwnership`\
   4.3. `getPhoto`

## 1) Hình nền <a href="#id-1-background" id="id-1-background"></a>

We will make a simple contract called "Klaystagram".

* `PhotoData` struct is defined to store various photo data.
* User can upload photo and transfer the ownership photo via `uploadPhoto` and `transferOwnership` functions.

## 2) Thiết lập hợp đồng <a href="#id-2-contract-setup" id="id-2-contract-setup"></a>

* Specify solidity version. We recommend using 0.5.6 stable version.
* We will make use of ERC721 standard to build non-fungible tokens.
  * Import `ERC721.sol` and `ERC721Enumerable.sol`
  * Check out detailed information about ERC721 at [erc721.org](http://erc721.org)

```
pragma solidity 0.5.6;

nhập "./ERC721/ERC721.sol";
nhập "./ERC721/ERC721Enumerable.sol";

hợp đồng Klaystagram là ERC721, ERC721Enumerable {
```

## 3) Đặt sự kiện và cấu trúc dữ liệu <a href="#id-3-set-events-and-data-structure" id="id-3-set-events-and-data-structure"></a>

We need to set up an event to keep track of activities on blockchain.

As for data structure, mapping `_photoList` takes a uint256 `tokenId` to map a specific `PhotoData` struct. By defining PhotoUploaded event, transaction receipt will log this event whenever function containing this is called.

```
event PhotoUploaded (uint indexed tokenId, bytes photo, string title, string location, string description, uint256 timestamp);

ánh xạ (uint256 => PhotoData) private _photoList;

struct PhotoData {
    uint256 tokenId;                       // id token không trùng lặp, bắt đầu từ 1 và tăng thêm 1
    address[] ownerHistory;                // Lịch sử tất cả những chủ sở hữu trước đây
    bytes photo;                           // Nguồn ảnh
    string title;                          // Tiêu đề ảnh
    string location;                       // Nơi chụp ảnh
    string description;                    // Mô tả ngắn về ảnh
    uint256 timestamp;                     // Thời gian tải lên
}
```

## 4) Viết hàm <a href="#id-4-write-functions" id="id-4-write-functions"></a>

Let's write some functions that interact with the contract. In this tutorial let us only consider two functions: `uploadPhoto` and `transferOwnership`. Check out Klaystagram.sol to see the whole set of functions.

### 4-1) `uploadPhoto` <a href="#id-4-1-uploadphoto" id="id-4-1-uploadphoto"></a>

`uploadPhoto` function takes 4 arguments including photo's image source. To keep things simple, `tokenId` will start from 1 and will increase by 1.

`_mint` function is from ERC721 contract. It creates a new token and assign it to a specific address, which in this case, `msg.sender`. In this application, logged in user will create transaction with their own private key. So `msg.sender` will be the user's public address.

Finally, initialize `PhotoData` struct, locate it inside `_photoList` mapping, and push the owner address into `ownerHistory` array. And don't forget to emit the event we just created. As mentioned above, this event will be included in transaction receipt.

```
function uploadPhoto(bytes memory photo, string memory title, string memory location, string memory description) public {
    uint256 tokenId = totalSupply() + 1;

    _mint(msg.sender, tokenId);

    address[] memory ownerHistory;

    PhotoData memory newPhotoData = PhotoData({
        tokenId : tokenId,
        ownerHistory : ownerHistory,
        photo : photo,
        title: title,
        location : location,
        description : description,
        timestamp : now
    });

    _photoList[tokenId] = newPhotoData;
    _photoList[tokenId].ownerHistory.push(msg.sender);

    emit PhotoUploaded(tokenId, photo, title, location, description, now);
}
```

### 4-2) `transferOwnership` <a href="#id-4-2-transferownership" id="id-4-2-transferownership"></a>

Let's take a look at `transferOwnership` function. When transferring photo ownership, we need to do two things. First, we have to reassign the owner, and then we have to push new owner address into `ownerHistory` array.

To do this, `transferOwnership` first calls `safeTransferFrom` function from ERC721 standard, which eventually calls `transferFrom` function. As mentioned above, right after token transfer is successfully done, we have to push new owner information into `ownerHistory` array, and that is exactly why `transferFrom` is overridden as below.

```
/**
  * @ghi chú hàm safeTransferFrom kiểm tra xem người nhận có thể xử lý token ERC721 không,
  * nhờ đó, ít có khả năng bị mất token. Sau khi kiểm tra xong, hàm transferFrom với định nghĩa bên dưới sẽ được gọi
  */
function transferOwnership(uint256 tokenId, address to) public returns(uint, address, address, address) {
    safeTransferFrom(msg.sender, to, tokenId);
    uint ownerHistoryLength = _photoList[tokenId].ownerHistory.length;
    return (
        _photoList[tokenId].tokenId,
        //chủ sở hữu ban đầu        _photoList[tokenId].ownerHistory[0],
        //người sở hữu trước đây, độ dài không thể nhỏ hơn 2
        _photoList[tokenId].ownerHistory[ownerHistoryLength-2],
        //chủ sở hữu hiện tại
        _photoList[tokenId].ownerHistory[ownerHistoryLength-1]);
}

/**
  * @notice Khuyên dùng transferOwnership có sử dụng hàm safeTransferFrom
  * @dev Viết đề lên hàm transferFrom để đảm bảo rằng mỗi lần chuyển quyền sở hữu
  *  địa chỉ của chủ sở hữu mới được đẩy vào mảng ownerHistory
  */
function transferFrom(address from, address to, uint256 tokenId) public {
    super.transferFrom(from, to, tokenId);
    _photoList[tokenId].ownerHistory.push(to);
}
```

### 4-3) `getPhoto` <a href="#id-4-3-getphoto" id="id-4-3-getphoto"></a>

Finally, let's make a getter function that fetches data stored in the smart contract. By calling a single function, we want to fetch every information regarding a specific photo. So `getPhoto` function takes an index(token id) as an argument and returns every element in PhotoData struct.

```
function getPhoto(uint tokenId) public view
returns(uint256, address[] memory, bytes memory, string memory, string memory, string memory, uint256) {
    require(_photoList[tokenId].tokenId != 0, "Ảnh không tồn tại");
    return (
        _photoList[tokenId].tokenId,
        _photoList[tokenId].ownerHistory,
        _photoList[tokenId].photo,
        _photoList[tokenId].title,
        _photoList[tokenId].location,
        _photoList[tokenId].description,
        _photoList[tokenId].timestamp);
}
```

This is it, now we can deploy this contract!
