mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-09 14:18:04 -05:00
feat: (WIP) Refactored meta transactions into a library. Includes hardhat-tracer.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import {ILensHub} from '../interfaces/ILensHub.sol';
|
||||
|
||||
import {Events} from '../libraries/Events.sol';
|
||||
import {Helpers} from '../libraries/Helpers.sol';
|
||||
import {Constants} from '../libraries/Constants.sol';
|
||||
@@ -11,7 +12,9 @@ import {Errors} from '../libraries/Errors.sol';
|
||||
import {PublishingLogic} from '../libraries/PublishingLogic.sol';
|
||||
import {ProfileTokenURILogic} from '../libraries/ProfileTokenURILogic.sol';
|
||||
import {InteractionLogic} from '../libraries/InteractionLogic.sol';
|
||||
import {LensNFTBase} from './base/LensNFTBase.sol';
|
||||
import {MetaTxLib} from '../libraries/MetaTxLib.sol';
|
||||
|
||||
import {LensHubNFTBase} from './base/LensHubNFTBase.sol';
|
||||
import {LensMultiState} from './base/LensMultiState.sol';
|
||||
import {LensHubStorage} from './storage/LensHubStorage.sol';
|
||||
import {VersionedInitializable} from '../upgradeability/VersionedInitializable.sol';
|
||||
@@ -29,7 +32,13 @@ import {IERC721Enumerable} from '@openzeppelin/contracts/token/ERC721/extensions
|
||||
* 1. Both Follow & Collect NFTs invoke an LensHub callback on transfer with the sole purpose of emitting an event.
|
||||
* 2. Almost every event in the protocol emits the current block timestamp, reducing the need to fetch it manually.
|
||||
*/
|
||||
contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHubStorage, ILensHub {
|
||||
contract LensHub is
|
||||
LensHubNFTBase,
|
||||
VersionedInitializable,
|
||||
LensMultiState,
|
||||
LensHubStorage,
|
||||
ILensHub
|
||||
{
|
||||
uint256 internal constant REVISION = 1;
|
||||
|
||||
address internal immutable FOLLOW_NFT_IMPL;
|
||||
@@ -140,6 +149,27 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
/// *****PROFILE OWNER FUNCTIONS*****
|
||||
/// *********************************
|
||||
|
||||
function permit(
|
||||
address spender,
|
||||
uint256 tokenId,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external {
|
||||
MetaTxLib.permit(spender, tokenId, sig);
|
||||
}
|
||||
|
||||
function permitForAll(
|
||||
address owner,
|
||||
address operator,
|
||||
bool approved,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external {
|
||||
MetaTxLib.permitForAll(owner, operator, approved, sig);
|
||||
}
|
||||
|
||||
function getDomainSeparator() external view returns (bytes32) {
|
||||
return MetaTxLib.getDomainSeparator();
|
||||
}
|
||||
|
||||
/// @inheritdoc ILensHub
|
||||
function createProfile(DataTypes.CreateProfileData calldata vars)
|
||||
external
|
||||
@@ -173,24 +203,8 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
|
||||
vars.wallet,
|
||||
vars.profileId,
|
||||
sigNonces[vars.wallet]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.wallet,
|
||||
vars.sig
|
||||
);
|
||||
_setDefaultProfile(vars.wallet, vars.profileId);
|
||||
}
|
||||
MetaTxLib.baseSetDefaultProfileWithSig(vars);
|
||||
_setDefaultProfile(vars.wallet, vars.profileId);
|
||||
}
|
||||
|
||||
/// @inheritdoc ILensHub
|
||||
@@ -215,25 +229,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
keccak256(vars.followModuleInitData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseSetFollowModuleWithSig(vars);
|
||||
PublishingLogic.setFollowModule(
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
@@ -255,24 +251,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DISPATCHER_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.dispatcher,
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseSetDispatcherWithSig(vars);
|
||||
_setDispatcher(vars.profileId, vars.dispatcher);
|
||||
}
|
||||
|
||||
@@ -292,24 +271,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.imageURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseSetProfileImageURIWithSig(vars);
|
||||
_setProfileImageURI(vars.profileId, vars.imageURI);
|
||||
}
|
||||
|
||||
@@ -329,24 +291,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.followNFTURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseSetFollowNFTURIWithSig(vars);
|
||||
_setFollowNFTURI(vars.profileId, vars.followNFTURI);
|
||||
}
|
||||
|
||||
@@ -376,28 +321,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
whenPublishingEnabled
|
||||
returns (uint256)
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
POST_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.contentURI)),
|
||||
vars.collectModule,
|
||||
keccak256(vars.collectModuleInitData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.basePostWithSig(vars);
|
||||
return
|
||||
_createPost(
|
||||
vars.profileId,
|
||||
@@ -427,31 +351,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
whenPublishingEnabled
|
||||
returns (uint256)
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COMMENT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.contentURI)),
|
||||
vars.profileIdPointed,
|
||||
vars.pubIdPointed,
|
||||
keccak256(vars.referenceModuleData),
|
||||
vars.collectModule,
|
||||
keccak256(vars.collectModuleInitData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseCommentWithSig(vars);
|
||||
return
|
||||
_createComment(
|
||||
DataTypes.CommentData(
|
||||
@@ -486,28 +386,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
whenPublishingEnabled
|
||||
returns (uint256)
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
MIRROR_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.profileIdPointed,
|
||||
vars.pubIdPointed,
|
||||
keccak256(vars.referenceModuleData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseMirrorWithSig(vars);
|
||||
return
|
||||
_createMirror(
|
||||
DataTypes.MirrorData(
|
||||
@@ -541,11 +420,11 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
* the NFT.
|
||||
*/
|
||||
function burnWithSig(uint256 tokenId, DataTypes.EIP712Signature calldata sig)
|
||||
public
|
||||
override
|
||||
external
|
||||
whenNotPaused
|
||||
{
|
||||
super.burnWithSig(tokenId, sig);
|
||||
MetaTxLib.baseBurnWithSig(tokenId, sig);
|
||||
_burn(tokenId);
|
||||
_clearHandleHash(tokenId);
|
||||
}
|
||||
|
||||
@@ -577,31 +456,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
whenNotPaused
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
uint256 dataLength = vars.datas.length;
|
||||
bytes32[] memory dataHashes = new bytes32[](dataLength);
|
||||
for (uint256 i = 0; i < dataLength; ) {
|
||||
dataHashes[i] = keccak256(vars.datas[i]);
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
FOLLOW_WITH_SIG_TYPEHASH,
|
||||
keccak256(abi.encodePacked(vars.profileIds)),
|
||||
keccak256(abi.encodePacked(dataHashes)),
|
||||
sigNonces[vars.follower]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.follower,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseFollowWithSig(vars);
|
||||
return
|
||||
InteractionLogic.follow(
|
||||
vars.follower,
|
||||
@@ -637,24 +492,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
|
||||
whenNotPaused
|
||||
returns (uint256)
|
||||
{
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COLLECT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.pubId,
|
||||
keccak256(vars.data),
|
||||
sigNonces[vars.collector]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.collector,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
MetaTxLib.baseCollectWithSig(vars);
|
||||
return
|
||||
InteractionLogic.collect(
|
||||
vars.collector,
|
||||
|
||||
135
contracts/core/base/LensHubNFTBase.sol
Normal file
135
contracts/core/base/LensHubNFTBase.sol
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import {ILensHubNFTBase} from '../../interfaces/ILensHubNFTBase.sol';
|
||||
import {Errors} from '../../libraries/Errors.sol';
|
||||
import {DataTypes} from '../../libraries/DataTypes.sol';
|
||||
import {Events} from '../../libraries/Events.sol';
|
||||
import {ERC721Time} from './ERC721Time.sol';
|
||||
import {ERC721Enumerable} from './ERC721Enumerable.sol';
|
||||
|
||||
/**
|
||||
* @title LensNFTBase
|
||||
* @author Lens Protocol
|
||||
*
|
||||
* @dev This is a trimmed down version of the LensNFTBase, mostly for contract size concerns.
|
||||
* Meta transaction functions have been moved to the core LensHub to utilize the MetaTxLib library.
|
||||
*
|
||||
* @notice This is an abstract base contract to be inherited by other Lens Protocol NFTs, it includes
|
||||
* the slightly modified ERC721Enumerable, which itself inherits from the ERC721Time-- which adds an
|
||||
* internal operator approval setter, stores the mint timestamp for each token, and replaces the
|
||||
* constructor with an initializer.
|
||||
*/
|
||||
abstract contract LensHubNFTBase is ERC721Enumerable, ILensHubNFTBase {
|
||||
mapping(address => uint256) public sigNonces;
|
||||
|
||||
/**
|
||||
* @notice Initializer sets the name, symbol and the cached domain separator.
|
||||
*
|
||||
* NOTE: Inheritor contracts *must* call this function to initialize the name & symbol in the
|
||||
* inherited ERC721 contract.
|
||||
*
|
||||
* @param name The name to set in the ERC721 contract.
|
||||
* @param symbol The symbol to set in the ERC721 contract.
|
||||
*/
|
||||
function _initialize(string calldata name, string calldata symbol) internal {
|
||||
ERC721Time.__ERC721_Init(name, symbol);
|
||||
|
||||
emit Events.BaseInitialized(name, symbol, block.timestamp);
|
||||
}
|
||||
|
||||
/// @inheritdoc ILensNFTBase
|
||||
// function permit(
|
||||
// address spender,
|
||||
// uint256 tokenId,
|
||||
// DataTypes.EIP712Signature calldata sig
|
||||
// ) external override {
|
||||
// if (spender == address(0)) revert Errors.ZeroSpender();
|
||||
// address owner = ownerOf(tokenId);
|
||||
// unchecked {
|
||||
// _validateRecoveredAddress(
|
||||
// _calculateDigest(
|
||||
// keccak256(
|
||||
// abi.encode(
|
||||
// PERMIT_TYPEHASH,
|
||||
// spender,
|
||||
// tokenId,
|
||||
// sigNonces[owner]++,
|
||||
// sig.deadline
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// owner,
|
||||
// sig
|
||||
// );
|
||||
// }
|
||||
// _approve(spender, tokenId);
|
||||
// }
|
||||
|
||||
/// @inheritdoc ILensNFTBase
|
||||
// function permitForAll(
|
||||
// address owner,
|
||||
// address operator,
|
||||
// bool approved,
|
||||
// DataTypes.EIP712Signature calldata sig
|
||||
// ) external override {
|
||||
// if (operator == address(0)) revert Errors.ZeroSpender();
|
||||
// unchecked {
|
||||
// _validateRecoveredAddress(
|
||||
// _calculateDigest(
|
||||
// keccak256(
|
||||
// abi.encode(
|
||||
// PERMIT_FOR_ALL_TYPEHASH,
|
||||
// owner,
|
||||
// operator,
|
||||
// approved,
|
||||
// sigNonces[owner]++,
|
||||
// sig.deadline
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// owner,
|
||||
// sig
|
||||
// );
|
||||
// }
|
||||
// _setOperatorApproval(owner, operator, approved);
|
||||
// }
|
||||
|
||||
// / @inheritdoc ILensNFTBase
|
||||
// function getDomainSeparator() external view override returns (bytes32) {
|
||||
// return _calculateDomainSeparator();
|
||||
// }
|
||||
|
||||
/// @inheritdoc ILensHubNFTBase
|
||||
function burn(uint256 tokenId) public virtual override {
|
||||
if (!_isApprovedOrOwner(msg.sender, tokenId)) revert Errors.NotOwnerOrApproved();
|
||||
_burn(tokenId);
|
||||
}
|
||||
|
||||
/// @inheritdoc ILensNFTBase
|
||||
// function burnWithSig(uint256 tokenId, DataTypes.EIP712Signature calldata sig)
|
||||
// public
|
||||
// virtual
|
||||
// override
|
||||
// {
|
||||
// address owner = ownerOf(tokenId);
|
||||
// unchecked {
|
||||
// _validateRecoveredAddress(
|
||||
// _calculateDigest(
|
||||
// keccak256(
|
||||
// abi.encode(
|
||||
// BURN_WITH_SIG_TYPEHASH,
|
||||
// tokenId,
|
||||
// sigNonces[owner]++,
|
||||
// sig.deadline
|
||||
// )
|
||||
// )
|
||||
// ),
|
||||
// owner,
|
||||
// sig
|
||||
// );
|
||||
// }
|
||||
// _burn(tokenId);
|
||||
// }
|
||||
}
|
||||
@@ -55,7 +55,7 @@ abstract contract LensNFTBase is ERC721Enumerable, ILensNFTBase {
|
||||
address spender,
|
||||
uint256 tokenId,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external override {
|
||||
) external virtual override {
|
||||
if (spender == address(0)) revert Errors.ZeroSpender();
|
||||
address owner = ownerOf(tokenId);
|
||||
unchecked {
|
||||
|
||||
70
contracts/interfaces/ILensHubNFTBase.sol
Normal file
70
contracts/interfaces/ILensHubNFTBase.sol
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import {DataTypes} from '../libraries/DataTypes.sol';
|
||||
|
||||
/**
|
||||
* @title ILensNFTBase
|
||||
* @author Lens Protocol
|
||||
*
|
||||
* @notice This is the interface for the LensNFTBase contract, from which all Lens NFTs inherit.
|
||||
* It is an expansion of a very slightly modified ERC721Enumerable contract, which allows expanded
|
||||
* meta-transaction functionality.
|
||||
*/
|
||||
interface ILensHubNFTBase {
|
||||
// /**
|
||||
// * @notice Implementation of an EIP-712 permit function for an ERC-721 NFT. We don't need to check
|
||||
// * if the tokenId exists, since the function calls ownerOf(tokenId), which reverts if the tokenId does
|
||||
// * not exist.
|
||||
// *
|
||||
// * @param spender The NFT spender.
|
||||
// * @param tokenId The NFT token ID to approve.
|
||||
// * @param sig The EIP712 signature struct.
|
||||
// */
|
||||
// function permit(
|
||||
// address spender,
|
||||
// uint256 tokenId,
|
||||
// DataTypes.EIP712Signature calldata sig
|
||||
// ) external;
|
||||
|
||||
// /**
|
||||
// * @notice Implementation of an EIP-712 permit-style function for ERC-721 operator approvals. Allows
|
||||
// * an operator address to control all NFTs a given owner owns.
|
||||
// *
|
||||
// * @param owner The owner to set operator approvals for.
|
||||
// * @param operator The operator to approve.
|
||||
// * @param approved Whether to approve or revoke approval from the operator.
|
||||
// * @param sig The EIP712 signature struct.
|
||||
// */
|
||||
// function permitForAll(
|
||||
// address owner,
|
||||
// address operator,
|
||||
// bool approved,
|
||||
// DataTypes.EIP712Signature calldata sig
|
||||
// ) external;
|
||||
|
||||
/**
|
||||
* @notice Burns an NFT, removing it from circulation and essentially destroying it. This function can only
|
||||
* be called by the NFT to burn's owner.
|
||||
*
|
||||
* @param tokenId The token ID of the token to burn.
|
||||
*/
|
||||
function burn(uint256 tokenId) external;
|
||||
|
||||
// /**
|
||||
// * @notice Implementation of an EIP-712 permit-style function for token burning. Allows anyone to burn
|
||||
// * a token on behalf of the owner with a signature.
|
||||
// *
|
||||
// * @param tokenId The token ID of the token to burn.
|
||||
// * @param sig The EIP712 signature struct.
|
||||
// */
|
||||
// function burnWithSig(uint256 tokenId, DataTypes.EIP712Signature calldata sig) external;
|
||||
|
||||
// /**
|
||||
// * @notice Returns the domain separator for this NFT contract.
|
||||
// *
|
||||
// * @return bytes32 The domain separator.
|
||||
// */
|
||||
// function getDomainSeparator() external view returns (bytes32);
|
||||
}
|
||||
536
contracts/libraries/MetaTxLib.sol
Normal file
536
contracts/libraries/MetaTxLib.sol
Normal file
@@ -0,0 +1,536 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import {DataTypes} from './DataTypes.sol';
|
||||
import {Errors} from './Errors.sol';
|
||||
|
||||
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
|
||||
|
||||
/**
|
||||
* @title MetaTxLib
|
||||
*
|
||||
* @author Lens Protocol (Zer0dot)
|
||||
*
|
||||
* @notice This library includes functions pertaining to meta-transactions to be called
|
||||
* specifically from the LensHub.
|
||||
*
|
||||
* @dev The functions `getDomainSeparator`, `permit` and `permitForAll` have been absolutely delegated
|
||||
* to this contract, but other `withSig` non-standard functions have only had their signature
|
||||
* validation and nonce increment delegated to this contract.
|
||||
*
|
||||
* @dev It's also important to note that the _ownerOf() function does not validate against the zero
|
||||
* address since the _validateRecoveredAddress() function reverts on a recovered zero address.
|
||||
*/
|
||||
library MetaTxLib {
|
||||
// We store constants equal to the storage slots here to later access via inline
|
||||
// assembly without needing to pass storage pointers. The NAME_SLOT_GT_31 slot
|
||||
// is equivalent to keccak256(NAME_SLOT) and is where the name string is stored
|
||||
// if the length is greater than 31 bytes.
|
||||
uint256 internal constant NAME_SLOT = 0;
|
||||
uint256 internal constant TOKEN_DATA_MAPPING_SLOT = 2;
|
||||
uint256 internal constant APPROVAL_MAPPING_SLOT = 4;
|
||||
uint256 internal constant OPERATOR_APPROVAL_MAPPING_SLOT = 5;
|
||||
uint256 internal constant SIG_NONCES_MAPPING_SLOT = 10;
|
||||
uint256 internal constant NAME_SLOT_GT_31 =
|
||||
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563;
|
||||
|
||||
// We also store typehashes here
|
||||
bytes32 internal constant EIP712_REVISION_HASH = keccak256('1');
|
||||
bytes32 internal constant PERMIT_TYPEHASH =
|
||||
keccak256('Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)');
|
||||
bytes32 internal constant PERMIT_FOR_ALL_TYPEHASH =
|
||||
keccak256(
|
||||
'PermitForAll(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant BURN_WITH_SIG_TYPEHASH =
|
||||
keccak256('BurnWithSig(uint256 tokenId,uint256 nonce,uint256 deadline)');
|
||||
bytes32 internal constant EIP712_DOMAIN_TYPEHASH =
|
||||
keccak256(
|
||||
'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
|
||||
);
|
||||
bytes32 internal constant SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'SetDefaultProfileWithSig(address wallet,uint256 profileId,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'SetFollowModuleWithSig(uint256 profileId,address followModule,bytes followModuleInitData,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'SetFollowNFTURIWithSig(uint256 profileId,string followNFTURI,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant SET_DISPATCHER_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'SetDispatcherWithSig(uint256 profileId,address dispatcher,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'SetProfileImageURIWithSig(uint256 profileId,string imageURI,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant POST_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'PostWithSig(uint256 profileId,string contentURI,address collectModule,bytes collectModuleInitData,address referenceModule,bytes referenceModuleInitData,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant COMMENT_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'CommentWithSig(uint256 profileId,string contentURI,uint256 profileIdPointed,uint256 pubIdPointed,bytes referenceModuleData,address collectModule,bytes collectModuleInitData,address referenceModule,bytes referenceModuleInitData,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant MIRROR_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'MirrorWithSig(uint256 profileId,uint256 profileIdPointed,uint256 pubIdPointed,bytes referenceModuleData,address referenceModule,bytes referenceModuleInitData,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant FOLLOW_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'FollowWithSig(uint256[] profileIds,bytes[] datas,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
bytes32 internal constant COLLECT_WITH_SIG_TYPEHASH =
|
||||
keccak256(
|
||||
'CollectWithSig(uint256 profileId,uint256 pubId,bytes data,uint256 nonce,uint256 deadline)'
|
||||
);
|
||||
|
||||
function permit(
|
||||
address spender,
|
||||
uint256 tokenId,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external {
|
||||
if (spender == address(0)) revert Errors.ZeroSpender();
|
||||
address owner = _ownerOf(tokenId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(PERMIT_TYPEHASH, spender, tokenId, _sigNonces(owner), sig.deadline)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
_approve(spender, tokenId);
|
||||
}
|
||||
|
||||
function permitForAll(
|
||||
address owner,
|
||||
address operator,
|
||||
bool approved,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external {
|
||||
if (operator == address(0)) revert Errors.ZeroSpender();
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_FOR_ALL_TYPEHASH,
|
||||
owner,
|
||||
operator,
|
||||
approved,
|
||||
_sigNonces(owner),
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
_setOperatorApproval(owner, operator, approved);
|
||||
}
|
||||
|
||||
function baseSetDefaultProfileWithSig(DataTypes.SetDefaultProfileWithSigData calldata vars)
|
||||
external
|
||||
{
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
|
||||
vars.wallet,
|
||||
vars.profileId,
|
||||
_sigNonces(vars.wallet),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.wallet,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseSetFollowModuleWithSig(DataTypes.SetFollowModuleWithSigData calldata vars)
|
||||
external
|
||||
{
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
keccak256(vars.followModuleInitData),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseSetDispatcherWithSig(DataTypes.SetDispatcherWithSigData calldata vars) external {
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DISPATCHER_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.dispatcher,
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseSetProfileImageURIWithSig(DataTypes.SetProfileImageURIWithSigData calldata vars)
|
||||
external
|
||||
{
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.imageURI)),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseSetFollowNFTURIWithSig(DataTypes.SetFollowNFTURIWithSigData calldata vars)
|
||||
external
|
||||
{
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.followNFTURI)),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function basePostWithSig(DataTypes.PostWithSigData calldata vars) external {
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
unchecked {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
POST_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.contentURI)),
|
||||
vars.collectModule,
|
||||
keccak256(vars.collectModuleInitData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function baseCommentWithSig(DataTypes.CommentWithSigData calldata vars) external {
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COMMENT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.contentURI)),
|
||||
vars.profileIdPointed,
|
||||
vars.pubIdPointed,
|
||||
keccak256(vars.referenceModuleData),
|
||||
vars.collectModule,
|
||||
keccak256(vars.collectModuleInitData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseMirrorWithSig(DataTypes.MirrorWithSigData calldata vars) external {
|
||||
address owner = _ownerOf(vars.profileId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
MIRROR_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.profileIdPointed,
|
||||
vars.pubIdPointed,
|
||||
keccak256(vars.referenceModuleData),
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleInitData),
|
||||
_sigNonces(owner),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseBurnWithSig(uint256 tokenId, DataTypes.EIP712Signature calldata sig) external {
|
||||
address owner = _ownerOf(tokenId);
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
BURN_WITH_SIG_TYPEHASH,
|
||||
tokenId,
|
||||
_sigNonces(owner),
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseFollowWithSig(DataTypes.FollowWithSigData calldata vars) external {
|
||||
uint256 dataLength = vars.datas.length;
|
||||
bytes32[] memory dataHashes = new bytes32[](dataLength);
|
||||
for (uint256 i = 0; i < dataLength; ) {
|
||||
dataHashes[i] = keccak256(vars.datas[i]);
|
||||
unchecked {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
FOLLOW_WITH_SIG_TYPEHASH,
|
||||
keccak256(abi.encodePacked(vars.profileIds)),
|
||||
keccak256(abi.encodePacked(dataHashes)),
|
||||
_sigNonces(vars.follower),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.follower,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function baseCollectWithSig(DataTypes.CollectWithSigData calldata vars) external {
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COLLECT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.pubId,
|
||||
keccak256(vars.data),
|
||||
_sigNonces(vars.collector),
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
),
|
||||
vars.collector,
|
||||
vars.sig
|
||||
);
|
||||
}
|
||||
|
||||
function getDomainSeparator() external view returns (bytes32) {
|
||||
return _calculateDomainSeparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Wrapper for ecrecover to reduce code size, used in meta-tx specific functions.
|
||||
*/
|
||||
function _validateRecoveredAddress(
|
||||
bytes32 digest,
|
||||
address expectedAddress,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) private view {
|
||||
if (sig.deadline < block.timestamp) revert Errors.SignatureExpired();
|
||||
address recoveredAddress = ecrecover(digest, sig.v, sig.r, sig.s);
|
||||
if (recoveredAddress == address(0) || recoveredAddress != expectedAddress)
|
||||
revert Errors.SignatureInvalid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates EIP712 DOMAIN_SEPARATOR based on the current contract and chain ID.
|
||||
*/
|
||||
function _calculateDomainSeparator() private view returns (bytes32) {
|
||||
return
|
||||
keccak256(
|
||||
abi.encode(
|
||||
EIP712_DOMAIN_TYPEHASH,
|
||||
keccak256(_nameBytes()),
|
||||
EIP712_REVISION_HASH,
|
||||
block.chainid,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates EIP712 digest based on the current DOMAIN_SEPARATOR.
|
||||
*
|
||||
* @param hashedMessage The message hash from which the digest should be calculated.
|
||||
*
|
||||
* @return bytes32 A 32-byte output representing the EIP712 digest.
|
||||
*/
|
||||
function _calculateDigest(bytes32 hashedMessage) private view returns (bytes32) {
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked('\x19\x01', _calculateDomainSeparator(), hashedMessage)
|
||||
);
|
||||
}
|
||||
return digest;
|
||||
}
|
||||
|
||||
function _nameBytes() private view returns (bytes memory) {
|
||||
bytes memory ptr;
|
||||
assembly {
|
||||
// Load the free memory pointer, where we'll return the string
|
||||
ptr := mload(64)
|
||||
|
||||
// Load the slot, which either contains the name + 2*length if length < 32 or
|
||||
// 2*length+1 if length >= 32, and the actual string starts at slot keccak256(NAME_SLOT)
|
||||
let slotLoad := sload(NAME_SLOT)
|
||||
|
||||
// Determine if the length > 32 by checking the lowest order bit, meaning the string
|
||||
// itself is stored at keccak256(NAME_SLOT)
|
||||
let isNotSameSlot := and(slotLoad, 1)
|
||||
|
||||
switch isNotSameSlot
|
||||
case 0 {
|
||||
// The name is in the same slot
|
||||
// Determine the size by dividing the last byte's value by 2
|
||||
let size := shr(1, and(slotLoad, 255))
|
||||
|
||||
// Store the size in the first slot
|
||||
mstore(ptr, size)
|
||||
|
||||
// Store the actual string in the second slot (without the size)
|
||||
mstore(add(ptr, 32), and(slotLoad, not(255)))
|
||||
|
||||
// Store the new memory pointer in the free memory pointer slot
|
||||
mstore(64, add(add(ptr, 32), size))
|
||||
}
|
||||
case 1 {
|
||||
// The name is not in the same slot
|
||||
// Determine the size by dividing the value in the whole slot minus 1 by 2
|
||||
let size := shr(1, sub(slotLoad, 1))
|
||||
|
||||
// Store the size in the first slot
|
||||
mstore(ptr, size)
|
||||
|
||||
// Compute the total memory slots we need, this is (size + 31) / 32
|
||||
let totalMemorySlots := shr(5, add(size, 31))
|
||||
|
||||
// Iterate through the words in memory and store the string word by word
|
||||
for {
|
||||
let i := 0
|
||||
} lt(i, totalMemorySlots) {
|
||||
i := add(i, 1)
|
||||
} {
|
||||
mstore(add(add(ptr, 32), mul(32, i)), sload(add(NAME_SLOT_GT_31, i)))
|
||||
}
|
||||
|
||||
// Store the new memory pointer in the free memory pointer slot
|
||||
mstore(64, add(add(ptr, 32), size))
|
||||
}
|
||||
}
|
||||
// Return a memory pointer to the name (which always starts with the size at the first slot)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function _sigNonces(address owner) private returns (uint256) {
|
||||
uint256 previousValue;
|
||||
assembly {
|
||||
mstore(0, owner)
|
||||
mstore(32, SIG_NONCES_MAPPING_SLOT)
|
||||
let slot := keccak256(0, 64)
|
||||
previousValue := sload(slot)
|
||||
sstore(slot, add(previousValue, 1))
|
||||
}
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
function _approve(address spender, uint256 tokenId) private {
|
||||
assembly {
|
||||
mstore(0, tokenId)
|
||||
mstore(32, APPROVAL_MAPPING_SLOT)
|
||||
let slot := keccak256(0, 64)
|
||||
sstore(slot, spender)
|
||||
}
|
||||
}
|
||||
|
||||
function _setOperatorApproval(
|
||||
address owner,
|
||||
address operator,
|
||||
bool approved
|
||||
) private {
|
||||
assembly {
|
||||
mstore(0, owner)
|
||||
mstore(32, OPERATOR_APPROVAL_MAPPING_SLOT)
|
||||
mstore(32, keccak256(0, 64))
|
||||
mstore(0, operator)
|
||||
let slot := keccak256(0, 64)
|
||||
sstore(slot, approved)
|
||||
}
|
||||
}
|
||||
|
||||
function _ownerOf(uint256 tokenId) private view returns (address) {
|
||||
// Note that this does *not* include a zero address check, but this is acceptable because
|
||||
// _validateRecoveredAddress reverts on recovering a zero address.
|
||||
address owner;
|
||||
assembly {
|
||||
mstore(0, tokenId)
|
||||
mstore(32, TOKEN_DATA_MAPPING_SLOT)
|
||||
let slot := keccak256(0, 64)
|
||||
// this weird bit shift is necessary to remove the packing from the variable
|
||||
owner := shr(96, shl(96, sload(slot)))
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import 'hardhat-gas-reporter';
|
||||
import 'hardhat-contract-sizer';
|
||||
import 'hardhat-log-remover';
|
||||
import 'hardhat-spdx-license-identifier';
|
||||
import 'hardhat-tracer';
|
||||
|
||||
if (!process.env.SKIP_LOAD) {
|
||||
glob.sync('./tasks/**/*.ts').forEach(function (file) {
|
||||
@@ -23,7 +24,6 @@ if (!process.env.SKIP_LOAD) {
|
||||
});
|
||||
}
|
||||
|
||||
const DEFAULT_BLOCK_GAS_LIMIT = 12450000;
|
||||
const MNEMONIC_PATH = "m/44'/60'/0'/0";
|
||||
const MNEMONIC = process.env.MNEMONIC || '';
|
||||
const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
|
||||
@@ -73,11 +73,6 @@ const config: HardhatUserConfig = {
|
||||
mumbai: getCommonNetworkConfig(ePolygonNetwork.mumbai, 80001),
|
||||
xdai: getCommonNetworkConfig(eXDaiNetwork.xdai, 100),
|
||||
hardhat: {
|
||||
hardfork: 'london',
|
||||
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
|
||||
gas: DEFAULT_BLOCK_GAS_LIMIT,
|
||||
gasPrice: 8000000000,
|
||||
chainId: HARDHATEVM_CHAINID,
|
||||
throwOnTransactionFailures: true,
|
||||
throwOnCallFailures: true,
|
||||
accounts: accounts.map(({ secretKey, balance }: { secretKey: string; balance: string }) => ({
|
||||
@@ -85,6 +80,7 @@ const config: HardhatUserConfig = {
|
||||
balance,
|
||||
})),
|
||||
forking: mainnetFork,
|
||||
allowUnlimitedContractSize: true,
|
||||
},
|
||||
},
|
||||
gasReporter: {
|
||||
|
||||
5602
package-lock.json
generated
5602
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,7 @@
|
||||
"hardhat-gas-reporter": "1.0.6",
|
||||
"hardhat-log-remover": "2.0.2",
|
||||
"hardhat-spdx-license-identifier": "2.0.3",
|
||||
"hardhat-tracer": "^1.1.0-rc.6",
|
||||
"husky": "7.0.4",
|
||||
"prettier": "2.5.0",
|
||||
"prettier-plugin-solidity": "1.0.0-beta.19",
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
ProfileFollowModule__factory,
|
||||
RevertFollowModule__factory,
|
||||
ProfileCreationProxy__factory,
|
||||
MetaTxLib__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployWithVerify, waitForTx } from './helpers/utils';
|
||||
|
||||
@@ -92,11 +93,17 @@ task('full-deploy-verify', 'deploys the entire Lens Protocol with explorer verif
|
||||
[],
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic'
|
||||
);
|
||||
const metaTxLib = await deployWithVerify(
|
||||
new MetaTxLib__factory(deployer).deploy({ nonce: deployerNonce++ }),
|
||||
[],
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib'
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib': metaTxLib.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
ProfileFollowModule__factory,
|
||||
RevertFollowModule__factory,
|
||||
ProfileCreationProxy__factory,
|
||||
MetaTxLib__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployContract, waitForTx } from './helpers/utils';
|
||||
|
||||
@@ -68,11 +69,15 @@ task('full-deploy', 'deploys the entire Lens Protocol').setAction(async ({}, hre
|
||||
const profileTokenURILogic = await deployContract(
|
||||
new ProfileTokenURILogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const metaTxLib = await deployContract(
|
||||
new MetaTxLib__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib': metaTxLib.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
|
||||
98
tasks/list-storage.ts
Normal file
98
tasks/list-storage.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { hexlify, keccak256, RLP } from 'ethers/lib/utils';
|
||||
import { task } from 'hardhat/config';
|
||||
import {
|
||||
LensHub__factory,
|
||||
PublishingLogic__factory,
|
||||
InteractionLogic__factory,
|
||||
ProfileTokenURILogic__factory,
|
||||
FollowNFT__factory,
|
||||
TransparentUpgradeableProxy__factory,
|
||||
MetaTxLib__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployContract, waitForTx } from './helpers/utils';
|
||||
|
||||
task('list-storage', '').setAction(async ({}, hre) => {
|
||||
const ethers = hre.ethers;
|
||||
const accounts = await ethers.getSigners();
|
||||
const deployer = accounts[0];
|
||||
const governance = accounts[1];
|
||||
const proxyAdminAddress = deployer.address;
|
||||
|
||||
// Nonce management in case of deployment issues
|
||||
let deployerNonce = await ethers.provider.getTransactionCount(deployer.address);
|
||||
|
||||
console.log('\n\t-- Deploying Logic Libs --');
|
||||
|
||||
const publishingLogic = await deployContract(
|
||||
new PublishingLogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const interactionLogic = await deployContract(
|
||||
new InteractionLogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const profileTokenURILogic = await deployContract(
|
||||
new ProfileTokenURILogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const metaTxLib = await deployContract(
|
||||
new MetaTxLib__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib': metaTxLib.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
// const nonce = await deployer.getTransactionCount();
|
||||
const followNFTNonce = hexlify(deployerNonce + 1);
|
||||
const collectNFTNonce = hexlify(deployerNonce + 2);
|
||||
const hubProxyNonce = hexlify(deployerNonce + 3);
|
||||
|
||||
const followNFTImplAddress =
|
||||
'0x' + keccak256(RLP.encode([deployer.address, followNFTNonce])).substr(26);
|
||||
const collectNFTImplAddress =
|
||||
'0x' + keccak256(RLP.encode([deployer.address, collectNFTNonce])).substr(26);
|
||||
const hubProxyAddress =
|
||||
'0x' + keccak256(RLP.encode([deployer.address, hubProxyNonce])).substr(26);
|
||||
|
||||
// Next, we deploy first the hub implementation, then the followNFT implementation, the collectNFT, and finally the
|
||||
// hub proxy with initialization.
|
||||
console.log('\n\t-- Deploying Hub Implementation --');
|
||||
|
||||
const lensHubImpl = await deployContract(
|
||||
new LensHub__factory(hubLibs, deployer).deploy(followNFTImplAddress, collectNFTImplAddress, {
|
||||
nonce: deployerNonce++,
|
||||
})
|
||||
);
|
||||
|
||||
console.log('\n\t-- Deploying Follow & Collect NFT Implementations --');
|
||||
await deployContract(
|
||||
new FollowNFT__factory(deployer).deploy(hubProxyAddress, { nonce: deployerNonce++ })
|
||||
);
|
||||
await deployContract(
|
||||
new FollowNFT__factory(deployer).deploy(hubProxyAddress, { nonce: deployerNonce++ })
|
||||
);
|
||||
|
||||
let data = lensHubImpl.interface.encodeFunctionData('initialize', [
|
||||
'Lens Protocol Profiles',
|
||||
'LPP',
|
||||
governance.address,
|
||||
]);
|
||||
|
||||
console.log('\n\t-- Deploying Hub Proxy --');
|
||||
let proxy = await deployContract(
|
||||
new TransparentUpgradeableProxy__factory(deployer).deploy(
|
||||
lensHubImpl.address,
|
||||
proxyAdminAddress,
|
||||
data,
|
||||
{ nonce: deployerNonce++ }
|
||||
)
|
||||
);
|
||||
|
||||
for (let i = 0; i < 100; ++i) {
|
||||
const storageSlot = await ethers.provider.getStorageAt(proxy.address, i);
|
||||
console.log(`Hub proxy storage at slot ${i}: ${storageSlot}`);
|
||||
}
|
||||
});
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
UIDataProvider__factory,
|
||||
ProfileFollowModule__factory,
|
||||
RevertFollowModule__factory,
|
||||
MetaTxLib__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployWithVerify, waitForTx } from './helpers/utils';
|
||||
|
||||
@@ -91,11 +92,17 @@ task(
|
||||
[],
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic'
|
||||
);
|
||||
const metaTxLib = await deployWithVerify(
|
||||
new MetaTxLib__factory(deployer).deploy({ nonce: deployerNonce++ }),
|
||||
[],
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib'
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib': metaTxLib.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbiCoder } from '@ethersproject/contracts/node_modules/@ethersproject/abi';
|
||||
import { AbiCoder } from 'ethers/lib/utils';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect, use } from 'chai';
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
ModuleGlobals__factory,
|
||||
ProfileTokenURILogic__factory,
|
||||
PublishingLogic__factory,
|
||||
MetaTxLib__factory,
|
||||
RevertCollectModule,
|
||||
RevertCollectModule__factory,
|
||||
TimedFeeCollectModule,
|
||||
@@ -167,11 +168,13 @@ before(async function () {
|
||||
const publishingLogic = await new PublishingLogic__factory(deployer).deploy();
|
||||
const interactionLogic = await new InteractionLogic__factory(deployer).deploy();
|
||||
const profileTokenURILogic = await new ProfileTokenURILogic__factory(deployer).deploy();
|
||||
const metaTxLib = await new MetaTxLib__factory(deployer).deploy();
|
||||
hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
'contracts/libraries/MetaTxLib.sol:MetaTxLib': metaTxLib.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { FollowNFT__factory } from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import hre from 'hardhat';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
@@ -699,6 +700,9 @@ makeSuiteCleanRoom('Publishing Comments', function () {
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
// hre.tracer.enabled = true;
|
||||
// hre.tracer.sloads = true;
|
||||
// hre.tracer.sstores = true;
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/bignumber';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/bignumber';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/bignumber';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/bignumber';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/bignumber';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import hre from 'hardhat';
|
||||
import { expect } from 'chai';
|
||||
import { keccak256, toUtf8Bytes } from 'ethers/lib/utils';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../helpers/constants';
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
user,
|
||||
userAddress,
|
||||
} from '../__setup.spec';
|
||||
import { hardhatArguments } from 'hardhat';
|
||||
|
||||
makeSuiteCleanRoom('Lens NFT Base Functionality', function () {
|
||||
context('generic', function () {
|
||||
@@ -104,7 +106,7 @@ makeSuiteCleanRoom('Lens NFT Base Functionality', function () {
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.ERC721_QUERY_FOR_NONEXISTENT_TOKEN);
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to permit with signature deadline mismatch', async function () {
|
||||
@@ -326,7 +328,7 @@ makeSuiteCleanRoom('Lens NFT Base Functionality', function () {
|
||||
);
|
||||
|
||||
await expect(lensHub.burnWithSig(0, { v, r, s, deadline: MAX_UINT256 })).to.be.revertedWith(
|
||||
ERRORS.ERC721_QUERY_FOR_NONEXISTENT_TOKEN
|
||||
ERRORS.SIGNATURE_INVALID
|
||||
);
|
||||
});
|
||||
|
||||
@@ -409,14 +411,18 @@ makeSuiteCleanRoom('Lens NFT Base Functionality', function () {
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.permit(userAddress, FIRST_PROFILE_ID, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
lensHub.permit(
|
||||
userAddress,
|
||||
FIRST_PROFILE_ID,
|
||||
{
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
{ gasLimit: 12450000 }
|
||||
)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
|
||||
Reference in New Issue
Block a user