# Lens для Solidity разработчиков

By [arsln.eth (🧙‍♂️, 🧪)](https://paragraph.com/@arsln) · 2024-01-05

---

Привет! Перед тем, как ты продолжишь, я должен предупредить тебя: я не являюсь гуру solidity разработке, у меня нет багажа в несколько лет опыта и я не знаком с Виталиком Бутериным, но мне интересно изучать как устроены различные web3/DeFi протоколы, логику и инфраструктуру их смарт-контрактов.

Я не буду говорить, что я знаю все о смарт-контрактах, но я обещаю, что вы узнаете много полезного и интересного из этого материала.

Цель этой работы - разобраться как работает Lens и поделиться своими знаниями с другими разработчиками, которые не знакомы с этим протоколом.

Я верю в идею "learn by public", поэтому создал этот материал для людей любого уровня в Solidity. Если вы junior или middle, то мои объяснения для каждого контракта будут полезны для вас. Если же вы advanced, то вы сможете двигаться со мной по порядку и углублять свои знания. Но не думайте, что этот материал будет скучным и тягучим, я постараюсь добавить немного юмора и интересных фактов, чтобы вы не заскучали.

Если вы заметили, что я где-то объяснил что-то неправильно или есть моменты, которые можно улучшить в этом материале, пожалуйста, сообщите мне об этом в телеграме. Я всегда открыт к обратной связи и готов улучшать свой материал для вас.

Часть 1: **Что такое Lens? 🌿**
-------------------------------

Я хотел рассказать вам о Lens Protocol и насколько это круто. Но, кажется, ребята из СНГ Lens сообщества меня уже опередили и написали классную статью об этом протоколе!)

Я настоятельно рекомендую тебе ее прочитать. Обрати внимание, что некоторые данные и статистики в статье могут быть актуальны на момент ее написания - 21 февраля.

[https://mirror.xyz/lensprotocolru.eth/jgKvqrez2VW8w4fjNjrezSo72XBbRFZEF1BJbqZrLFE](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];
        }
    }

---

*Originally published on [arsln.eth (🧙‍♂️, 🧪)](https://paragraph.com/@arsln/lens-solidity)*
