# 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)