# Lens для Solidity разработчиков **Published by:** [arsln.eth (🧙‍♂️, 🧪)](https://paragraph.com/@arsln/) **Published on:** 2024-01-05 **URL:** https://paragraph.com/@arsln/lens-solidity ## Content Привет! Перед тем, как ты продолжишь, я должен предупредить тебя: я не являюсь гуру solidity разработке, у меня нет багажа в несколько лет опыта и я не знаком с Виталиком Бутериным, но мне интересно изучать как устроены различные web3/DeFi протоколы, логику и инфраструктуру их смарт-контрактов. Я не буду говорить, что я знаю все о смарт-контрактах, но я обещаю, что вы узнаете много полезного и интересного из этого материала. Цель этой работы - разобраться как работает Lens и поделиться своими знаниями с другими разработчиками, которые не знакомы с этим протоколом. Я верю в идею "learn by public", поэтому создал этот материал для людей любого уровня в Solidity. Если вы junior или middle, то мои объяснения для каждого контракта будут полезны для вас. Если же вы advanced, то вы сможете двигаться со мной по порядку и углублять свои знания. Но не думайте, что этот материал будет скучным и тягучим, я постараюсь добавить немного юмора и интересных фактов, чтобы вы не заскучали. Если вы заметили, что я где-то объяснил что-то неправильно или есть моменты, которые можно улучшить в этом материале, пожалуйста, сообщите мне об этом в телеграме. Я всегда открыт к обратной связи и готов улучшать свой материал для вас.Часть 1: Что такое Lens? 🌿Я хотел рассказать вам о Lens Protocol и насколько это круто. Но, кажется, ребята из СНГ Lens сообщества меня уже опередили и написали классную статью об этом протоколе!) Я настоятельно рекомендую тебе ее прочитать. Обрати внимание, что некоторые данные и статистики в статье могут быть актуальны на момент ее написания - 21 февраля. https://mirror.xyz/lensprotocolru.eth/jgKvqrez2VW8w4fjNjrezSo72XBbRFZEF1BJbqZrLFE// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import {IFollowModule} from '../../../interfaces/IFollowModule.sol'; import {ILensHub} from '../../../interfaces/ILensHub.sol'; import {Errors} from '../../../libraries/Errors.sol'; import {FeeModuleBase} from '../FeeModuleBase.sol'; import {ModuleBase} from '../ModuleBase.sol'; import {FollowValidatorFollowModuleBase} from './FollowValidatorFollowModuleBase.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol'; /** * @notice A struct containing the necessary data to execute follow actions on a given profile. * * @param currency The currency associated with this profile. * @param amount The following cost associated with this profile. * @param recipient The recipient address associated with this profile. */ struct ProfileData { address currency; uint256 amount; address recipient; } /** * @title FeeFollowModule * @author Lens Protocol * * @notice This is a simple Lens FollowModule implementation, inheriting from the IFollowModule interface, but with additional * variables that can be controlled by governance, such as the governance & treasury addresses as well as the treasury fee. */ contract FeeFollowModule is FeeModuleBase, FollowValidatorFollowModuleBase { using SafeERC20 for IERC20; mapping(uint256 => ProfileData) internal _dataByProfile; constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {} /** * @notice This follow module levies a fee on follows. * * @param profileId The profile ID of the profile to initialize this module for. * @param data The arbitrary data parameter, decoded into: * address currency: The currency address, must be internally whitelisted. * uint256 amount: The currency total amount to levy. * address recipient: The custom recipient address to direct earnings to. * * @return bytes An abi encoded bytes parameter, which is the same as the passed data parameter. */ function initializeFollowModule(uint256 profileId, bytes calldata data) external override onlyHub returns (bytes memory) { (uint256 amount, address currency, address recipient) = abi.decode( data, (uint256, address, address) ); if (!_currencyWhitelisted(currency) || recipient == address(0) || amount == 0) revert Errors.InitParamsInvalid(); _dataByProfile[profileId].amount = amount; _dataByProfile[profileId].currency = currency; _dataByProfile[profileId].recipient = recipient; return data; } /** * @dev Processes a follow by: * 1. Charging a fee */ function processFollow( address follower, uint256 profileId, bytes calldata data ) external override onlyHub { uint256 amount = _dataByProfile[profileId].amount; address currency = _dataByProfile[profileId].currency; _validateDataIsExpected(data, currency, amount); (address treasury, uint16 treasuryFee) = _treasuryData(); address recipient = _dataByProfile[profileId].recipient; uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX; uint256 adjustedAmount = amount - treasuryAmount; IERC20(currency).safeTransferFrom(follower, recipient, adjustedAmount); if (treasuryAmount > 0) IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount); } /** * @dev We don't need to execute any additional logic on transfers in this follow module. */ function followModuleTransferHook( uint256 profileId, address from, address to, uint256 followNFTTokenId ) external override {} /** * @notice Returns the profile data for a given profile, or an empty struct if that profile was not initialized * with this module. * * @param profileId The token ID of the profile to query. * * @return ProfileData The ProfileData struct mapped to that profile. */ function getProfileData(uint256 profileId) external view returns (ProfileData memory) { return _dataByProfile[profileId]; } } ## Publication Information - [arsln.eth (🧙‍♂️, 🧪)](https://paragraph.com/@arsln/): Publication homepage - [All Posts](https://paragraph.com/@arsln/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@arsln): Subscribe to updates