feat: Initial implementation of PublicActProxy

This commit is contained in:
vicnaum
2023-09-22 18:48:55 +02:00
parent 8da496ae18
commit 09881059cb

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {ILensHub} from 'contracts/interfaces/ILensHub.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {CollectPublicationAction} from 'contracts/modules/act/collect/CollectPublicationAction.sol';
import {BaseProfilePublicationData, IBaseFeeCollectModule} from 'contracts/modules/interfaces/IBaseFeeCollectModule.sol';
import {MetaTxLib} from 'contracts/libraries/MetaTxLib.sol';
/// @title PublicActProxy
/// @author LensProtocol
/// @notice This contract allows anyone to Act on a publication without holding a profile
/// @dev This contract holds a profile and acts on behalf of the caller
contract PublicActProxy {
using SafeERC20 for IERC20;
ILensHub immutable HUB;
CollectPublicationAction immutable COLLECT_PUBLICATION_ACTION;
uint[9] private __gap;
mapping(address => uint256) private _nonces; // Slot 10 - to match with MetaTxLib/StorageLib
constructor(address lensHub, address collectPublicationAction) {
HUB = ILensHub(lensHub);
COLLECT_PUBLICATION_ACTION = CollectPublicationAction(collectPublicationAction);
}
/*
struct PublicationActionParams {
publicationActedProfileId: ---
publicationActedId: ---
actorProfileId: this contract's profile
referrerProfileIds: ---
referrerPubIds: ---
actionModuleAddress: ---
actionModuleData: {
collectNftRecipient: msg.sender
collectData: {
expectedCurrency: should match what's stored in CollectModule
expectedAmount: should match what's stored in CollectModule
}
}
}
*/
// This contract should be the owner of the publicationActionParams.actorProfileId
// This contract should be set as publicationActionParams.transactionExecutor
// Correct collectNftRecipient should be passed in the publicationActionParams.actionModuleData
// This is pretty simple, but should follow the rules above:
function publicFreeCollect(Types.PublicationActionParams calldata publicationActionParams) external {
HUB.act(publicationActionParams);
}
// For the paid collect to work, additional steps are required:
// Collector should set enough allowance to this contract to pay for collect NFT
// Funds will be taken from msg.sender
// Funds will be approved from this contract to collectModule found in publication storage
function publicCollect(Types.PublicationActionParams calldata publicationActionParams) external {
_publicCollect(publicationActionParams, msg.sender);
}
function publicCollectWithSig(
Types.PublicationActionParams calldata publicationActionParams,
Types.EIP712Signature calldata signature
) external {
// TODO: Consider moving this MetaTxLib out of LensHub domain somehow (because of nonces conflict)
// Reason: someone can use these signatures again to Act on behalf of this profile again on LensHub
MetaTxLib.validateActSignature(signature, publicationActionParams);
HUB.act(publicationActionParams);
_publicCollect(publicationActionParams, signature.signer);
}
function nonces(address signer) public view returns (uint256) {
return _nonces[signer];
}
function _publicCollect(
Types.PublicationActionParams calldata publicationActionParams,
address transactionExecutor
) internal {
address collectModule = COLLECT_PUBLICATION_ACTION
.getCollectData(
publicationActionParams.publicationActedProfileId,
publicationActionParams.publicationActedId
)
.collectModule;
BaseProfilePublicationData memory collectData = IBaseFeeCollectModule(collectModule).getBasePublicationData(
publicationActionParams.publicationActedProfileId,
publicationActionParams.publicationActedId
);
if (collectData.amount > 0) {
IERC20(collectData.currency).safeTransferFrom(transactionExecutor, address(this), collectData.amount);
IERC20(collectData.currency).safeIncreaseAllowance(collectModule, collectData.amount);
}
HUB.act(publicationActionParams);
// TODO: This might not be needed
if (collectData.amount > 0) {
IERC20(collectData.currency).approve(collectModule, 0);
}
}
}