# 4. Write Klaystagram Smart Contract

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

## 1) Background <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) 스마트 컨트랙트 설정 <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;

import "./ERC721/ERC721.sol";
import "./ERC721/ERC721Enumerable.sol";

contract Klaystagram is ERC721, ERC721Enumerable {
```

## 3) 이벤트 및 데이터 구조 설정 <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);

mapping (uint256 => PhotoData) private _photoList;

struct PhotoData {
    uint256 tokenId;                       // 1부터 차례로 1씩 증가하는 고유한 토큰 ID
    address[] ownerHistory;                // 이전 소유자들의 기록
    bytes photo;                           // 이미지의 소스
    string title;                          // 사진 제목
    string location;                       // 사진 촬영 장소
    string description;                    // 사진에 대한 간략한 설명
    uint256 timestamp;                     // 업로드된 시간
}
```

## 4) 함수 작성 <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.

```
/**
  * @notice safeTransferFrom 함수는 수신자가 ERC721 토큰을 처리할 수 있는지 체크하여 토큰이 유실되는 상황을 줄여줍니다. After checking is done, it will call transferFrom function defined below
  */
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,
        //original owner
        _photoList[tokenId].ownerHistory[0],
        //previous owner, length cannot be less than 2
        _photoList[tokenId].ownerHistory[ownerHistoryLength-2],
        //current owner
        _photoList[tokenId].ownerHistory[ownerHistoryLength-1]);
}

/**
  * @notice Recommend using transferOwnership, which uses safeTransferFrom function
  * @dev Override transferFrom function to make sure that every time ownership transfers
  *  new owner address gets pushed into ownerHistory array
  */
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, "Photo does not exist");
    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!
