---
# System prepended metadata

title: Lens Protocol 合約跟概念講解

---

# Lens Protocol 合約跟概念講解

# Overview

![](https://i.imgur.com/euGw5Kn.png)

## Profile 
- 個人檔案，相當於 Lens 當中的身分證，代表你這個人  
- 是一個 NFT  
- 可選擇要使用的 follow module  
- 可指定**dispatcher**，相當於賦予權利給某個 address，讓他代表這個 profile 去執行相關的動作(post  setFollowModule....等)  
## Publication 

### Post  
- 等同於社交軟體中的**貼文**  
- 包含採用的 reference & collect module  

### Comment  
- 等同於社交軟體中的**留言**  
- 這留言會去對應到相關的 post 跟 profile  
- 對應的方法是去記錄一個`Pointer`，因此會動用到 reference module 的功能  

### Mirror
- **引用別人的publication** -> post comment mirror(對！你可以mirror他的mirror)
- 不會有內容
- 如果`getContentURI(mirror)`的話，得到的contentURI會是mirror指到的那個publication的contentURI
- 只有reference module

    
## Follow  

- 等同於現在社群中的追隨或關注
- 對應關係是 profile  
- 可以去 follow 你想要的 profile  
- 可以得到相對應 profile 的 follow NFT，藉此證明你有追隨這個 profile  
- 在 follow 的時候會根據你想要 follow 的 profile 當中的 follow module 而有不同的實現邏輯  
- 可以有**治理**的功能，讓持有同一種 follow NFT 的人可以形成一個 DAO  

## Collect  
- 對應關係是 Publication  
- 可以 collect 其他 profile 跟 comment  

## Module  
- module 可以分為以下三種，所有的 module 都可以是自己另外部署的一個合約，並把需要的 init data 跟 address 設定進相對應的物件 
- 例如 
    - follow module 設定進 profile 當中  
    - reference & collect module 設定 publication 當中  
- 可以藉由 module 來設定自己的想要的 logic  
    ## Follow module  
    - **被設定在 profile 裡面**  
    - 一旦有人想要 follow 就會去調用 follow module 當中的邏輯  
    - 例如  
        - 用戶 A 設定一 follow module 在自己的 profile 中，此 module 的功能為需要打入**通關密碼**才能 follow  
        - 那麼用戶 B 就需要在 follow 的時候傳送相關的 data 給 follow function，這邊需要的 data 就是**通關密碼**  
        - 通關密碼正確才算 follow 成功，並得到 follow NFT  
    ## Collect module  
    - **被設定在 Publication**當中  
    - 使用方法與 follow module 類似  
    ## Reference module  
    - **被設定在Publication**當中
    - 當有**引用 refrence**的時候會使用到 reference module
    - 

***


# 核心合約講解
- 使用**typechain**跟**typechain-ethers**來生成 Contract factory 
- Lenshub 程式中大量運用**upgradable** **proxy** **create**等等的方式 
- 如果是 proxy 還有分 proxy 跟 implementation 兩個部分 
- 
## LensHub 
- 由`LensHub`當成核心合約，去跟其他的 library 以及合約做互動 
- `LensHub`採用`TransparentUpgradeableProxy` 
- 大多數主要執行動作的都交由`InteractionLogic` `ProfileTokenURILogic` `PublishingLogic` 這三個 library 完成，例如 collect、follow、createProfile 等等 

## Follow NFT  
- 繼承 LensNFTBase(from ERC721Enumerable) 
- 當其他使用者 follow 之後可以 mint 一個 Follow NFT 

## Collect NFT
- 跟 follow NFT 結構類似，繼承了相同的 LensNFTBase 
- 當 collect publication 後就可以 mint 一個相對應的 NFT 
- 
## InteractionLogic 

功能太多，以下只說明兩個 function 

### follow 
 ```solidity
//follow() 的程式片段
address followModule = _profileById[profileIds[i]].followModule;
address followNFT = _profileById[profileIds[i]].followNFT;

//如果被follow的profile的follow NFT address == 0的話就重新
//部署一個新的follow NFT contract
if (followNFT == address(0)) {
    followNFT = _deployFollowNFT(profileIds[i]);
    _profileById[profileIds[i]].followNFT = followNFT;
}

//部署完畢後用address去調用followNFT的interface去mint
tokenIds[i] = IFollowNFT(followNFT).mint(follower);

//如果profile當中有設定follow module
//那就需要調用follow module當中的processFollow來處理相關的邏輯
if (followModule != address(0)) {
    IFollowModule(followModule).processFollow(
        follower,
        profileIds[i],
        followModuleDatas[i]
    );
}
```
```solidity
function _deployFollowNFT(uint256 profileId) private returns (address) {
    bytes memory functionData = abi.encodeWithSelector(
        IFollowNFT.initialize.selector,
        profileId
    );

    //創建一個NFT Proxy後執行initialize()
    address followNFT = address(new FollowNFTProxy(functionData));
    emit Events.FollowNFTDeployed(profileId, followNFT, block.timestamp);

    return followNFT;
}
```

follow 不同的 profile 會產生**邏輯需一樣但儲存狀態不一樣的**合約，所以會需要 proxy 來執行 implementation contract 

不重新部署一個全新的合約原因推測是重新部署的成本太大，所以部署一個相對成本較小的 proxy contract 來執行 implementation contract，因為implementation contract 是透過 proxy 啟用，所以implementation的constructor不會動到 ，取而代之的是調用`initialize`來達到初始化的功能 

### Collect

結構與follow()類似，

```solidity
//collect 程式片段

//如果collect的是mirror
//那麼必須先找出mirror根源的profile publication collectModule

(uint256 rootProfileId, uint256 rootPubId, address rootCollectModule) = Helpers
    .getPointedIfMirror(profileId, pubId, _pubByIdByProfile);

uint256 tokenId;
// Avoids stack too deep
{
    address collectNFT = _pubByIdByProfile[rootProfileId][rootPubId].collectNFT;
    
    if (collectNFT == address(0)) {
        collectNFT = _deployCollectNFT(
            rootProfileId,
            rootPubId,
            _profileById[rootProfileId].handle,
            collectNFTImpl
        );
        _pubByIdByProfile[rootProfileId][rootPubId].collectNFT = collectNFT;
    }
    tokenId = ICollectNFT(collectNFT).mint(collector);
}


//如果collect一個mirror的話，那就調用mirror(被collect)指向的publication的collect module
ICollectModule(rootCollectModule).processCollect(
    profileId,
    collector,
    rootProfileId,
    rootPubId,
    collectModuleData
);
```
```solidity
function _deployCollectNFT(
    uint256 profileId,
    uint256 pubId,
    string memory handle,
    address collectNFTImpl
) private returns (address) {
    
    //採用Openzeppelin的Clones以節省成本
    address collectNFT = Clones.clone(collectNFTImpl);

    bytes4 firstBytes = bytes4(bytes(handle));

    string memory collectNFTName = string(
        abi.encodePacked(handle, Constants.COLLECT_NFT_NAME_INFIX, pubId.toString())
    );
    string memory collectNFTSymbol = string(
        abi.encodePacked(firstBytes, Constants.COLLECT_NFT_SYMBOL_INFIX, pubId.toString())
    );

    ICollectNFT(collectNFT).initialize(profileId, pubId, collectNFTName, collectNFTSymbol);
    emit Events.CollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);

    return collectNFT;
}

```

***

# 測試流程說明

## 情境測試1 一般使用狀況

假設有三個用戶user userTwo userThree

1. user 跟 userTwo 分別create兩個profile
2. userTwo發了一篇文 -> publicaton ID 1
3. user在userTwo ID為1的publication(post)底下留言(comment)，user產生一publication(comment ID 1)
4. userTwo覺得user的留言很中肯，mirror起來，此時userTwo有兩個publication，分別為post(ID 1) 跟mirror(ID 2)
5. 此時userThree想collect userTwo的post(ID 1)，但必須先追隨userTwo，因為post的collect module設定為，必須先追隨userTwo才能collect
6. userThree接下來想collect userTwo的mirror(ID 2)，因為此mirror指向的是user的comment，所以執行被指向的comment的collect module(也就是必須先追隨user才能collect)
7. 最後檢查userThree有沒有成功獲得collect NFT


## 情境測試2 自定義module

製作一個follow module ->`FollowOtherModule.sol`
功能是假設有一用戶想follow userTwo，那必須先follow user，並將這個follow module放入userTwo的profile中

reference module採用內部預設的合約 ->`FollowerOnlyReferenceModule.sol` 
功能是必須先follow才能執行reference相關功能(comment mirror)