Merge branch 'development' into misc/svgs-v3

This commit is contained in:
Alan
2024-04-02 15:56:15 +01:00
committed by GitHub
26 changed files with 3545 additions and 95 deletions

View File

@@ -149,95 +149,144 @@
"ProfileTokenURI": "0xCCF77B802160326282F260bb6e275333fEA9E76C",
"HandleTokenURI": "0x33b7C0692DD8267f936936C0A0f7079144d78B92",
"FollowTokenURI": "0x1a4D3f97770925A14997B351C5cC3Cd47192a5B8"
"PermissionlessCreatorImpl": "0xCBC26052907AEC42878870B330cA141264E56205",
"PermissionlessCreator": "0x0b5e6100243f793e480DE6088dE6bA70aA9f3872"
},
"testnet": {
"chainId": 80001,
"network": "mumbai",
"LensProfilesGuardianTimelock": 300,
"LensHandlesGuardianTimelock": 300,
"LensHubProxy": "0x60Ae865ee4C725cd04353b5AAb364553f56ceF82",
"ModuleGlobals": "0x1353aAdfE5FeD85382826757A95DE908bd21C4f9",
"LensHubProxy": "0x4fbffF20302F3326B20052ab9C217C44F6480900",
"ModuleGlobals": "0x19E75039d17D580b66bD0Eb52cF011B2966A8582",
"ProfileCreator": "0x6C1e1bC39b13f9E0Af9424D76De899203F47755F",
"LensPeriphery": "0xD5037d72877808cdE7F669563e9389930AF404E8",
"LensPeriphery": "0xFf7Ba2334dD8D50067c664561ffFddc7aecEA870",
"UIDataProvider": "0x2BcFaaCb0450d63860E3Ee7c62ea12330BdC96F5",
"WMATIC": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
"Modules": {
"v1": {
"collect": [
{
"name": "FreeCollectModule",
"addy": "0x0BE6bD7092ee83D44a6eC1D949626FeE48caB30c"
"addy": "0x41564F744244D4221B87e4570A351F507f774245"
},
{
"name": "FeeCollectModule",
"addy": "0xeb4f3EC9d01856Cec2413bA5338bF35CeF932D82"
"addy": "0x04DdbF98fA7D795A2c522fe501Eff5AdA68e0703"
},
{
"name": "LimitedFeeCollectModule",
"addy": "0xFCDA2801a31ba70dfe542793020a934F880D54aB"
},
{
"name": "TimedFeeCollectModule",
"addy": "0x36447b496ebc97DDA6d8c8113Fe30A30dC0126Db"
},
{
"name": "LimitedTimedFeeCollectModule",
"addy": "0xDa76E44775C441eF53B9c769d175fB2948F15e1C"
},
{
"name": "RevertCollectModule",
"addy": "0x5E70fFD2C6D04d65C3abeBa64E93082cfA348dF8"
"addy": "0x0206b4123E2fdec8F98BC31132e13928a793D676"
},
{
"name": "SimpleFeeCollectModule",
"addy": "0xb4a9874adc790eec88fd086e43d329bbc9520efd"
"addy": "0x01C43c11161B7A1960f95595445c8449a9c356D5"
},
{
"name": "MultirecipientFeeCollectModule",
"addy": "0x99d6c3eabf05435e851c067d2c3222716f7fcfe5"
"addy": "0xBA377571F81D7225A5744911932aB5451b4Ca5d7"
},
{
"name": "StepwiseCollectModule",
"addy": "0x7a7b8e7699e0492da1d3c7eab7e2f3bf1065aa40"
},
{
"name": "ERC4626FeeCollectModule",
"addy": "0x79697402bd2caa19a53d615fb1a30a98e35b84d5"
},
{
"name": "AaveFeeCollectModule",
"addy": "0x912860ed4ed6160c48a52d52fcab5c059d34fe5a"
"addy": "0xfdf89C33b36331f7568a1885bcCf2AfaCe44084e"
}
],
"follow": [
{
"name": "FeeFollowModule",
"addy": "0xe7AB9BA11b97EAC820DbCc861869092b52B65C06"
"addy": "0x0000000000000000000000000000000000000000"
},
{
"name": "ProfileFollowModule",
"addy": "0x8c32203df6b1A04E25145346e2DaAD0B4712C20D"
"addy": "0x0000000000000000000000000000000000000000"
},
{
"name": "RevertFollowModule",
"addy": "0x8c822Fc029EBdE62Da1Ed1072534c5e112dAE48c"
"addy": "0x0000000000000000000000000000000000000000"
}
],
"reference": [
{
"name": "TokenGatedReferenceModule",
"addy": "0xb4ba8dccd35bd3dcc5d58dbb9c7dff9c9268add9"
"addy": "0xD7aE30dCD7bc74F98562C2E64B3762058289C884"
},
{
"name": "FollowerOnlyReferenceModule",
"addy": "0x7Ea109eC988a0200A1F79Ae9b78590F92D357a16"
"addy": "0x50c1E6f739ad1b7ADaCdf848656682e4644aF42F"
},
{
"name": "DegreesOfSeparationReferenceModule",
"addy": "0xe20D64D25779D2Ae0d76711e5Aca23EE633f2E1E"
"addy": "0xA740cb1f188FfF87b232cc9F5f27c473E1653d6A"
}
]
},
"v2": {}
"v2": {
"follow": [
{
"name": "FeeFollowModule",
"addy": "0xB7612562C63dDcAFE4349beE6Ca35e9170819504"
},
{
"name": "RevertFollowModule",
"addy": "0xaaB7E564ECbb2D11bCb9592A18F2E9281BA9f73A"
}
],
"act": [
{
"name": "CollectPublicationAction",
"addy": "0x4FdAae7fC16Ef41eAE8d8f6578d575C9d64722f2"
}
],
"collect": [
{
"name": "SimpleFeeCollectModule",
"addy": "0x345Cc3A3F9127DE2C69819C2E07bB748dE6E45ee"
},
{
"name": "MultirecipientFeeCollectModule",
"addy": "0xe9FabdC429640DC35153C9Ba13AE83A0A1996912"
}
],
"reference": [
{
"name": "DegreesOfSeparationReferenceModule",
"addy": "0xFb290857DBA5c24Ef548a8D98CEFba8E56293254"
},
{
"name": "FollowerOnlyReferenceModule",
"addy": "0x9Dd0b6e39F8Ea8B8dE9D55acC4652573E2eCB16f"
}
]
}
},
"Treasury": "0x8c6a6488822a5Dad91CBBfFAF7Af3379Bd6d8306",
"TreasuryFee": "50",
"LegacyCollectNFTImpl": "0x712d529Fe13c075069d97A878243e3E5A826D012",
"FollowNFTImpl": "0xC2452BAAeffd6109750401bF83e15E5aaec98606",
"LensHandlesImpl": "0x4d2c60431D4Fb2484C82fdD8C05985FfF93851E1",
"LensHandles": "0x44e1668150A154f01D53d1f05B8Fa0d8f3f341a9",
"TokenHandleRegistryImpl": "0xe3a704FEe3260cbee9B54A300be53f534Ad6755A",
"TokenHandleRegistry": "0x66cA05cAc4e8D543028192dD28C0570a0b6e3413",
"ModuleRegistryImpl": "0xBa7b1C57e6168b2B910b24F26FEbe41B4552A47e",
"ModuleRegistry": "0x4BeB63842BB800A1Da77a62F2c74dE3CA39AF7C0",
"LensHubV2Impl": "0xb4A26f55Cc2d1473b8A7649d90d34ba52A480391",
"GovernanceContract": "0x6D90D6c3F66784f5b5AFEb68f856592e4ad158c3",
"GovernanceContractAdmin": "0x1A1cDf59C94a682a067fA2D288C2167a8506abd7",
"ProxyAdminContract": "0xfbAddb4efA038325800846238e928529b7DD4a67",
"ProxyAdminContractAdmin": "0xcB6C7b2E340D50701d45d55507f19A5cE5d72330",
"LensV2UpgradeContract": "0x0Bd9617289e79751a9e1a0a19232608E1F7C36A4",
"ProfileCreationProxy": "0x0554a7163C3aa423429719940FFE179F21cD83f6",
"PublicActProxyImpl": "0x8555ad9CaCf2DE9873C15ed8369FC8BE7E2675FA",
"PublicActProxy": "0x4ed64Eb32C96Df0eA60BB8934798F4DFd3098Ba1",
"CollectNFT": "0x15062A8b16EAe79D68EC6a583212349bdF679C5e",
"CollectPublicationActionImpl": "0x2f422811ABBc5AFA6d18f083e70Bc6326fD245E0",
"LitAccessControlImpl": "0x6F0512aC88DDEB0A4bE8EeCC716daC0793EeCff2",
"LitAccessControl": "0xc4F726a10fDEb0E98e16Fa658b606192d57FC71c",
"AnonymousProfileId": "3",
"FollowTokenURI": "0xB9136f393861822cF2CAEa7E7062f6c71354d46f",
"PermissionlessCreatorImpl": "0xBb841c444655558176aBf97993b7f653A2B24994",
"PermissionlessCreator": "0xCb4FB63c3f13CB83cCD6F10E9e5F29eC250329Cc",
"FreeCreditsProvider": "0xC3161EeE3A055860125eE0b70412e4e4081852dD",
"ProfileTokenURI": "0x19787A2E8b396077B99F381c39B2DB5781FF6D38"
},
"sandbox": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -129,6 +129,41 @@ abstract contract LensProfiles is LensBaseERC721, ERC2981CollectionRoyalties, IL
LensBaseERC721.supportsInterface(interfaceId) || ERC2981CollectionRoyalties.supportsInterface(interfaceId);
}
function transferFromKeepingDelegates(address from, address to, uint256 tokenId) external {
//solhint-disable-next-line max-line-length
if (!_isApprovedOrOwner(msg.sender, tokenId)) {
revert Errors.NotOwnerOrApproved();
}
if (!StorageLib.profileCreatorWhitelisted()[msg.sender]) {
// Delegates can be maintained on transfers only when executed by whitelisted profile creators, which are
// trusted entities, for the sake of a better onboarding UX.
revert Errors.NotAllowed();
}
if (ownerOf(tokenId) != from) {
revert Errors.InvalidOwner();
}
if (to == address(0)) {
revert Errors.InvalidParameter();
}
_beforeTokenTransferWithoutClearingDelegates(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
unchecked {
--StorageLib.balances()[from];
++StorageLib.balances()[to];
}
StorageLib.getTokenData(tokenId).owner = to;
emit Transfer(from, to, tokenId);
}
function _hasTokenGuardianEnabled(address wallet) internal view returns (bool) {
return
!wallet.isContract() &&
@@ -159,4 +194,16 @@ abstract contract LensProfiles is LensBaseERC721, ERC2981CollectionRoyalties, IL
}
super._beforeTokenTransfer(from, to, tokenId);
}
function _beforeTokenTransferWithoutClearingDelegates(
address from,
address to,
uint256 tokenId
) internal whenNotPaused {
if (from != address(0) && _hasTokenGuardianEnabled(from)) {
// Cannot transfer profile if the guardian is enabled, except at minting time.
revert Errors.GuardianEnabled();
}
super._beforeTokenTransfer(from, to, tokenId);
}
}

View File

@@ -7,9 +7,9 @@ import {Errors} from 'contracts/libraries/constants/Errors.sol';
import {TransparentUpgradeableProxy} from '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';
contract LensVersion is ILensVersion {
string internal constant version = '2.0.3';
string internal constant version = '2.0.4';
bytes20 internal constant gitCommit = hex'3bb1438b28b69f584ab9a290f261e3452fd34ad0';
bytes20 internal constant gitCommit = hex'91fb968ff155ed065dad4146f65a1a8c45b8f922';
event LensUpgradeVersion(address implementation, string version, bytes20 gitCommit, uint256 timestamp);

View File

@@ -29,4 +29,9 @@ interface ILensProfiles is ILensERC721 {
* @return uint256 The timestamp at which the Token Guardian will become effectively disabled. Zero if enabled.
*/
function getTokenGuardianDisablingTimestamp(address wallet) external view returns (uint256);
/**
* @notice allows transferring of profile but keeping the delegate settings
*/
function transferFromKeepingDelegates(address from, address to, uint256 tokenId) external;
}

View File

@@ -8,7 +8,7 @@ library StorageLib {
// uint256 constant NAME_SLOT = 0;
// uint256 constant SYMBOL_SLOT = 1;
uint256 constant TOKEN_DATA_MAPPING_SLOT = 2;
// uint256 constant BALANCES_SLOT = 3;
uint256 constant BALANCES_SLOT = 3;
// uint256 constant TOKEN_APPROVAL_MAPPING_SLOT = 4;
// uint256 constant OPERATOR_APPROVAL_MAPPING_SLOT = 5;
// Slot 6 is deprecated in Lens V2. In V1 it was used for ERC-721 Enumerable's `ownedTokens`.
@@ -114,6 +114,12 @@ library StorageLib {
}
}
function balances() internal pure returns (mapping(address => uint256) storage _balances) {
assembly {
_balances.slot := BALANCES_SLOT
}
}
function blockedStatus(
uint256 blockerProfileId
) internal pure returns (mapping(uint256 => bool) storage _blockedStatus) {

View File

@@ -49,4 +49,6 @@ library Errors {
// Migration Errors
error NotMigrationAdmin();
error NotAllowed();
}

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {PermissionlessCreator} from 'contracts/misc/PermissionlessCreator.sol';
contract FreeCreditsProvider {
PermissionlessCreator permissionlessCreator;
constructor(address permissionlessCreator_) {
permissionlessCreator = PermissionlessCreator(permissionlessCreator_);
}
function getFreeCredit(address user, uint256 amount) external {
return permissionlessCreator.increaseCredits(user, amount);
}
function burnCredits(uint256 amount) external {
return permissionlessCreator.decreaseCredits(msg.sender, amount);
}
}

View File

@@ -0,0 +1,344 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {ILensHub} from 'contracts/interfaces/ILensHub.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
import {ILensHandles} from 'contracts/interfaces/ILensHandles.sol';
import {ITokenHandleRegistry} from 'contracts/interfaces/ITokenHandleRegistry.sol';
import {ImmutableOwnable} from 'contracts/misc/ImmutableOwnable.sol';
/**
* @title PermissonlessCreator
* @author Lens Protocol
* @notice This is an ownable public proxy contract which is open for all.
*/
contract PermissionlessCreator is ImmutableOwnable {
ILensHandles public immutable LENS_HANDLES;
ITokenHandleRegistry public immutable TOKEN_HANDLE_REGISTRY;
// These should be configured through setters before being whitelisted in the LensHub.
uint128 private _profileCreationCost;
uint128 private _handleCreationCost;
uint8 private _handleLengthMin;
mapping(address => uint256) internal _credits;
mapping(address => bool) internal _isCreditProvider; // Credit providers can increase/decrease credits of users
mapping(address => bool) internal _isUntrusted;
mapping(uint256 => address) internal _profileCreatorUsingCredits; // The address that created the profile spending credits
modifier onlyCreditProviders() {
if (!_isCreditProvider[msg.sender]) {
revert OnlyCreditProviders();
}
_;
}
error OnlyCreditProviders();
error HandleAlreadyExists();
error InvalidFunds();
error InsufficientCredits();
error ProfileAlreadyLinked();
error HandleLengthNotAllowed();
error NotAllowed();
event HandleCreationPriceChanged(uint256 newPrice, uint256 timestamp);
event ProfileCreationPriceChanged(uint256 newPrice, uint256 timestamp);
event HandleLengthMinChanged(uint8 newMinLength, uint256 timestamp);
event CreditBalanceChanged(address indexed creditAddress, uint256 remainingCredits, uint256 timestamp);
event TrustStatusChanged(address indexed targetAddress, bool isUntrusted, uint256 timestamp);
event CreditProviderStatusChanged(address indexed creditProvider, bool isCreditProvider, uint256 timestamp);
event ProfileCreatedUsingCredits(uint256 indexed profileId, address indexed creator, uint256 timestamp);
event HandleCreatedUsingCredits(
uint256 indexed handleId,
string handle,
address indexed creator,
uint256 timestamp
);
constructor(
address owner,
address lensHub,
address lensHandles,
address tokenHandleRegistry
) ImmutableOwnable(owner, lensHub) {
LENS_HANDLES = ILensHandles(lensHandles);
TOKEN_HANDLE_REGISTRY = ITokenHandleRegistry(tokenHandleRegistry);
}
/////////////////////////// Permissionless payable creation functions //////////////////////////////////////////////
function createProfile(
Types.CreateProfileParams calldata createProfileParams,
address[] calldata delegatedExecutors
) external payable returns (uint256) {
_validatePayment(_profileCreationCost);
// delegatedExecutors are only allowed if to == msg.sender
if (delegatedExecutors.length > 0 && createProfileParams.to != msg.sender) {
revert NotAllowed();
}
return _createProfile(createProfileParams, delegatedExecutors);
}
function createHandle(address to, string calldata handle) external payable returns (uint256) {
_validatePayment(_handleCreationCost);
_validateHandleLength(handle);
return LENS_HANDLES.mintHandle(to, handle);
}
function createProfileWithHandle(
Types.CreateProfileParams calldata createProfileParams,
string calldata handle,
address[] calldata delegatedExecutors
) external payable returns (uint256, uint256) {
_validatePayment(_profileCreationCost + _handleCreationCost);
_validateHandleLength(handle);
// delegatedExecutors are only allowed if to == msg.sender
if (delegatedExecutors.length > 0 && createProfileParams.to != msg.sender) {
revert NotAllowed();
}
return _createProfileWithHandle(createProfileParams, handle, delegatedExecutors);
}
////////////////////////////// Credit based creation functions /////////////////////////////////////////////////////
function createProfileUsingCredits(
Types.CreateProfileParams calldata createProfileParams,
address[] calldata delegatedExecutors
) external returns (uint256) {
_spendCredit(msg.sender);
uint256 profileId = _createProfile(createProfileParams, delegatedExecutors);
_profileCreatorUsingCredits[profileId] = msg.sender;
emit ProfileCreatedUsingCredits(profileId, msg.sender, block.timestamp);
return profileId;
}
function createProfileWithHandleUsingCredits(
Types.CreateProfileParams calldata createProfileParams,
string calldata handle,
address[] calldata delegatedExecutors
) external returns (uint256, uint256) {
_spendCredit(msg.sender);
_validateHandleLength(handle);
(uint256 profileId, uint256 handleId) = _createProfileWithHandle(
createProfileParams,
handle,
delegatedExecutors
);
_profileCreatorUsingCredits[profileId] = msg.sender;
emit ProfileCreatedUsingCredits(profileId, msg.sender, block.timestamp);
emit HandleCreatedUsingCredits(handleId, handle, msg.sender, block.timestamp);
return (profileId, handleId);
}
function createHandleUsingCredits(address to, string calldata handle) external returns (uint256) {
_spendCredit(msg.sender);
_validateHandleLength(handle);
uint256 handleId = LENS_HANDLES.mintHandle(to, handle);
emit HandleCreatedUsingCredits(handleId, handle, msg.sender, block.timestamp);
return handleId;
}
////////////////////////////////////////// Base functions //////////////////////////////////////////////////////////
function _createProfile(
Types.CreateProfileParams calldata createProfileParams,
address[] memory delegatedExecutors
) internal returns (uint256) {
uint256 profileId;
if (delegatedExecutors.length == 0) {
profileId = ILensHub(LENS_HUB).createProfile(createProfileParams);
} else {
// We mint the profile to this contract first, then apply delegates if defined
// This will not allow to initialize follow modules that require funds from the msg.sender,
// but we assume only simple follow modules should be set during profile creation.
// Complex ones can be set after the profile is created.
address destination = createProfileParams.to;
// Copy the struct from calldata to memory to make it mutable
Types.CreateProfileParams memory createProfileParamsMemory = createProfileParams;
createProfileParamsMemory.to = address(this);
profileId = ILensHub(LENS_HUB).createProfile(createProfileParamsMemory);
_addDelegatesToProfile(profileId, delegatedExecutors);
// keep the config if its been set
ILensHub(LENS_HUB).transferFromKeepingDelegates(address(this), destination, profileId);
}
return profileId;
}
function _createProfileWithHandle(
Types.CreateProfileParams calldata createProfileParams,
string calldata handle,
address[] memory delegatedExecutors
) private returns (uint256, uint256) {
// Copy the struct from calldata to memory to make it mutable
Types.CreateProfileParams memory createProfileParamsMemory = createProfileParams;
// We mint the handle & profile to this contract first, then link it to the profile and delegates if defined
// This will not allow to initialize follow modules that require funds from the msg.sender,
// but we assume only simple follow modules should be set during profile creation.
// Complex ones can be set after the profile is created.
address destination = createProfileParamsMemory.to;
createProfileParamsMemory.to = address(this);
uint256 profileId = ILensHub(LENS_HUB).createProfile(createProfileParamsMemory);
uint256 handleId = LENS_HANDLES.mintHandle(address(this), handle);
TOKEN_HANDLE_REGISTRY.link(handleId, profileId);
_addDelegatesToProfile(profileId, delegatedExecutors);
// Transfer the handle & profile to the destination
LENS_HANDLES.transferFrom(address(this), destination, handleId);
// keep the config if its been set
ILensHub(LENS_HUB).transferFromKeepingDelegates(address(this), destination, profileId);
return (profileId, handleId);
}
function _addDelegatesToProfile(uint256 profileId, address[] memory delegatedExecutors) private {
// set delegates if any
if (delegatedExecutors.length > 0) {
// Initialize an array of bools with the same length as delegatedExecutors
bool[] memory executorEnabled = new bool[](delegatedExecutors.length);
// Fill the array with `true`
for (uint256 i = 0; i < delegatedExecutors.length; i++) {
executorEnabled[i] = true;
}
ILensHub(LENS_HUB).changeDelegatedExecutorsConfig(profileId, delegatedExecutors, executorEnabled);
}
}
function _validateHandleLength(string calldata handle) private view {
if (bytes(handle).length < _handleLengthMin) {
revert HandleLengthNotAllowed();
}
}
function _validatePayment(uint256 amount) private view {
if (msg.value < amount) {
revert InvalidFunds();
}
}
function _spendCredit(address account) private {
_credits[account] -= 1;
emit CreditBalanceChanged(account, _credits[account], block.timestamp);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @notice Special function allowing to transfer a profile from one address to another, keeping the delegates.
/// @dev Requires the sender, a trusted credit-based creator, to approve the profile with this contract as spender.
function transferFromKeepingDelegates(address from, address to, uint256 tokenId) external {
if (_isUntrusted[msg.sender] || _profileCreatorUsingCredits[tokenId] != msg.sender) {
// If msg.sender trust was revoked or is not the original creator of the profile through credits, then fail.
revert NotAllowed();
}
ILensHub(LENS_HUB).transferFromKeepingDelegates(from, to, tokenId);
}
// Credit Provider functions
function increaseCredits(address account, uint256 amount) external onlyCreditProviders {
if (_isUntrusted[account]) {
// Cannot increase credits for an account with revoked trust.
revert NotAllowed();
}
_credits[account] += amount;
emit CreditBalanceChanged(account, _credits[account], block.timestamp);
}
function decreaseCredits(address account, uint256 amount) external onlyCreditProviders {
_credits[account] -= amount;
emit CreditBalanceChanged(account, _credits[account], block.timestamp);
}
// Owner functions
function withdrawFunds() external onlyOwner {
payable(OWNER).transfer(address(this).balance);
}
function addCreditProvider(address creditProvider) external onlyOwner {
_isCreditProvider[creditProvider] = true;
emit CreditProviderStatusChanged(creditProvider, true, block.timestamp);
}
function removeCreditProvider(address creditProvider) external onlyOwner {
_isCreditProvider[creditProvider] = false;
emit CreditProviderStatusChanged(creditProvider, false, block.timestamp);
}
function setProfileCreationPrice(uint128 newPrice) external onlyOwner {
_profileCreationCost = newPrice;
emit ProfileCreationPriceChanged(newPrice, block.timestamp);
}
function setHandleCreationPrice(uint128 newPrice) external onlyOwner {
_handleCreationCost = newPrice;
emit HandleCreationPriceChanged(newPrice, block.timestamp);
}
function setHandleLengthMin(uint8 newMinLength) external onlyOwner {
_handleLengthMin = newMinLength;
emit HandleLengthMinChanged(newMinLength, block.timestamp);
}
function setTrustStatus(address targetAddress, bool setAsUntrusted) external onlyOwner {
if (setAsUntrusted == _isUntrusted[targetAddress]) {
// No change in trust status.
return;
}
if (setAsUntrusted && _credits[targetAddress] > 0) {
// If it is becoming unstrusted, current credits should be removed.
_credits[targetAddress] = 0;
emit CreditBalanceChanged(targetAddress, 0, block.timestamp);
}
_isUntrusted[targetAddress] = setAsUntrusted;
emit TrustStatusChanged(targetAddress, setAsUntrusted, block.timestamp);
}
// View functions
function getProfileWithHandleCreationPrice() external view returns (uint256) {
return _profileCreationCost + _handleCreationCost;
}
function getProfileCreationPrice() external view returns (uint256) {
return _profileCreationCost;
}
function getHandleCreationPrice() external view returns (uint256) {
return _handleCreationCost;
}
function getHandleLengthMin() external view returns (uint8) {
return _handleLengthMin;
}
function isUntrusted(address targetAddress) external view returns (bool) {
return _isUntrusted[targetAddress];
}
function isCreditProvider(address targetAddress) external view returns (bool) {
return _isCreditProvider[targetAddress];
}
function getCreditBalance(address targetAddress) external view returns (uint256) {
return _credits[targetAddress];
}
function getProfileCreatorUsingCredits(uint256 profileId) external view returns (address) {
return _profileCreatorUsingCredits[profileId];
}
}

View File

@@ -13,8 +13,9 @@ optimizer_runs = 10
ignored_error_codes = []
no_match_path = "script/svg/*"
# via_ir = true
via_ir = true
# Mainnet libraries:
# libraries = [
# 'contracts/libraries/ActionLib.sol:ActionLib:0x7990dac84e3241fe314b980bba1466ac08715c4f',
# 'contracts/libraries/FollowLib.sol:FollowLib:0xe280cb21fb36b6b2d584428b809a6b822a5c2260',
@@ -27,55 +28,17 @@ no_match_path = "script/svg/*"
# 'contracts/libraries/ValidationLib.sol:ValidationLib:0x9cafd24d2851d9eb56e5a8fd394ab2ac0ef99849',
# ]
# ProfileSVG
# Testnet libraries:
# libraries = [
# 'contracts/libraries/svgs/Profile/Body/BodyHoodie.sol:BodyHoodie:0x7bc391b78f1ed3a2341faf915fb3edd3b5767e83',
# 'contracts/libraries/svgs/Profile/Body/BodyJacket.sol:BodyJacket:0xc6c1142fdd3f538c9435aff13d60e2339ba10160',
# 'contracts/libraries/svgs/Profile/Body/BodyShibuya.sol:BodyShibuya:0x9701be89b6ade39ae77ffbf03356eb137a31da7f',
# 'contracts/libraries/svgs/Profile/Body/BodyTShirt.sol:BodyTShirt:0xf02231c0319a004fcec98196ee42450a54521d7a',
# 'contracts/libraries/svgs/Profile/Body/BodyTanktop.sol:BodyTanktop:0xee94f5418c760f6306f1e8a461df1236074ab811',
# 'contracts/libraries/svgs/Profile/Body.sol:Body:0x1fd46ec18608ce20958b8513c305477b60d573d3',
# 'contracts/libraries/svgs/Profile/Face.sol:Face:0x0d8e2dbf398638ec60d90e9f120c9a0c2debf6f3',
# 'contracts/libraries/svgs/Profile/Face.sol:Face2:0xd418e23eb9333c76113028ccd6c840b4d6f287e7',
# 'contracts/libraries/svgs/Profile/Hands.sol:Hands:0xff7e415dd484b80df69ed32da055de9e95185605',
# 'contracts/libraries/svgs/Profile/Head.sol:Head:0xd53523fdb369aa75b34452e3f762471527225281',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBeanie.sol:HeadwearBeanie:0xebc5e2516050c0bbd44c83ebbcc074ddb1bab936',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBear.sol:HeadwearBear:0xc3c97e8ae72fecd1f2c1351196cb8124648151d5',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBee.sol:HeadwearBee:0xc6f26ae509e39657e408c8ced9b31f324506a3bb',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBirdie.sol:HeadwearBirdie:0xb3fa351ac9cd4c1d8e5723f8e061f56b61abf59a',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBrains.sol:HeadwearBrains:0x99a2505a8884cac0ab04e1726838bec65cf62ec6',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearBull.sol:HeadwearBull:0x8ef9b3be203c677d0e5bfbc5db11400d3afea821',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearCrown.sol:HeadwearCrown:0x6bbd83ca2ec3dbdcf96bd3d01262fb433f8f477b',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearEarrings.sol:HeadwearEarrings:0x4a92d520f527ead8f6f538ca0cbf9409d6a78f74',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearFloral.sol:HeadwearFloral:0x0c715cbfcc827d3861818f431a5c2299cf26b3eb',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearGlasses.sol:HeadwearGlasses:0xb3475daa34935b309a49741242bb2b4095204557',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearHat.sol:HeadwearHat:0x60f002d200b9fc5061b6c32682dced413113e65e',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearIcecream.sol:HeadwearIcecream:0x57d0e1f5ee84fcbf1d4adfb97e5c9a6b6f90b75a',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearLotus.sol:HeadwearLotus:0xb1b59c3eacedd5a648646825b1aba83f1fa44b7f',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearMajor.sol:HeadwearMajor:0x734e08dcc4dfdbefd1fe39eea94821ca0da1944f',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearMushroom.sol:HeadwearMushroom:0xe0be70c71f3d753119bfa686624c7fa3e4c94d1d',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearNightcap.sol:HeadwearNightcap:0x0e93c0412c55db27d4eb0d6cb9187c8de20bd33b',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearPartyhat.sol:HeadwearPartyhat:0x98a5505d61a2680220a6e9ae22c366a06ff1f2e5',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearPlants.sol:HeadwearPlants:0xa0c7ff9758e2a9518e39c09c19851e5e1fce669c',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearScout.sol:HeadwearScout:0xff286a64d376ba77fbe667fb88395c2818cbca74',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearShaman.sol:HeadwearShaman:0x6252c630824e4c4383f4675233e2fd62f1b224ed',
# 'contracts/libraries/svgs/Profile/Headwear/HeadwearSparkles.sol:HeadwearSparkles:0xae96209a41caa3042a979b61fe58c7030b06c826',
# 'contracts/libraries/svgs/Profile/Headwear.sol:Headwear:0x6f33a4a06ff39342fbd4a737fd3f53db92ffd038',
# 'contracts/libraries/svgs/Profile/Legs.sol:Legs:0x3c6a16c4f5aa3a06e7de06b590d69db5fc1b7a69',
# 'contracts/libraries/svgs/Profile/Logo.sol:Logo:0x6099032c8d4b93d25a50a8f0a6e2cc66259103bc',
# 'contracts/libraries/svgs/Profile/ProfileSVG.sol:ProfileSVG:0xf32f16af9e44cb92311de854d138547bc1f91dc7',
# 'contracts/libraries/svgs/Profile/Shoes.sol:Shoes:0x7289854ff2e55c43afcb895ec8cae798811fcc2d'
# ]
# HandleSVG
# libraries = [
# 'contracts/libraries/svgs/Handle/GintoNordFontSVG.sol:GintoNordFontSVG:0x68b2751c4113bbacca7c024a030355fe3989bda7',
# 'contracts/libraries/svgs/Handle/HandleSVG.sol:HandleSVG:0xa2574d9ddb6a325ad2be838bd854228b80215148'
# ]
# FollowSVG
# libraries = [
# 'contracts/libraries/svgs/Follow/FollowSVG.sol:FollowSVG:0x37cbd2c689a8eb10459d974a824adef74124dc7e'
# 'contracts/libraries/ActionLib.sol:ActionLib:0xf76565aee4bf3641bf10090561ff0143628fde99',
# 'contracts/libraries/FollowLib.sol:FollowLib:0xd33423b0ae94717274ef83431950a858b274c3f9',
# 'contracts/libraries/GovernanceLib.sol:GovernanceLib:0x06df1ff944ed3f3f140fb0afae5f27429f93eba1',
# 'contracts/libraries/LegacyCollectLib.sol:LegacyCollectLib:0xe6b308313eff636b82e7ced21cdc05ea7107504a',
# 'contracts/libraries/MetaTxLib.sol:MetaTxLib:0xf4a73d59b5c3eeb02d007e4f82c7952cc659c06f',
# 'contracts/libraries/MigrationLib.sol:MigrationLib:0x38fd9d33e9cdd1e99b4f9d983db64ed035ead9d4',
# 'contracts/libraries/ProfileLib.sol:ProfileLib:0xdf4bdf058490383c9f12a25c2f28679c8154aa8b',
# 'contracts/libraries/PublicationLib.sol:PublicationLib:0x70d2a21726bcc835e56680b173b31c38b6c33c64',
# 'contracts/libraries/ValidationLib.sol:ValidationLib:0xef1c3a9c87177217574afd66956d203ba8f0ee6c',
# ]
[rpc_endpoints]

254
script/DeployLensHub.s.sol Normal file
View File

@@ -0,0 +1,254 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {ForkManagement} from 'script/helpers/ForkManagement.sol';
import 'forge-std/Script.sol';
import {LibString} from 'solady/utils/LibString.sol';
import {FollowNFT} from 'contracts/FollowNFT.sol';
import {LensHubInitializable} from 'contracts/misc/LensHubInitializable.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
import {Governance} from 'contracts/misc/access/Governance.sol';
import {LensHandles} from 'contracts/namespaces/LensHandles.sol';
contract DeployLensHub is Script, ForkManagement {
using stdJson for string;
struct LensAccount {
uint256 ownerPk;
address owner;
uint256 profileId;
}
LensAccount _deployer;
string mnemonic;
uint256 internal PROFILE_GUARDIAN_COOLDOWN;
uint256 internal HANDLE_GUARDIAN_COOLDOWN;
address lensHub;
address legacyCollectNFTImpl;
address followNFTImpl;
address moduleRegistry;
address lensHandlesAddress;
address tokenHandleRegistryAddress;
address legacyFeeFollowModule;
address legacyProfileFollowModule;
address newFeeFollowModule;
address lensHandlesOwner;
address lensHandlesImpl;
address governanceContract;
address governanceAdmin;
address lensHubV2Impl;
string addressesFile = 'addressesV2.txt';
// TODO: Use from test/ContractAddresses
struct Module {
address addy;
string name;
}
// TODO: Move this somewhere common (also in UpgradeForkTest)
function findModuleHelper(
Module[] memory modules,
string memory moduleNameToFind
) internal pure returns (Module memory) {
for (uint256 i = 0; i < modules.length; i++) {
if (LibString.eq(modules[i].name, moduleNameToFind)) {
return modules[i];
}
}
revert('Module not found');
}
function saveContractAddress(string memory contractName, address deployedAddress) internal {
// console.log('Saving %s (%s) into addresses under %s environment', contractName, deployedAddress, targetEnv);
string[] memory inputs = new string[](5);
inputs[0] = 'node';
inputs[1] = 'script/helpers/saveAddress.js';
inputs[2] = targetEnv;
inputs[3] = contractName;
inputs[4] = vm.toString(deployedAddress);
// bytes memory res =
vm.ffi(inputs);
// string memory output = abi.decode(res, (string));
// console.log(output);
}
function loadPrivateKeys() internal {
if (isEnvSet('MNEMONIC')) {
mnemonic = vm.envString('MNEMONIC');
}
if (bytes(mnemonic).length == 0) {
revert('Missing mnemonic');
}
console.log('\n');
(_deployer.owner, _deployer.ownerPk) = deriveRememberKey(mnemonic, 0);
console.logBytes32(bytes32(_deployer.ownerPk));
console.log('Deployer address: %s', address(_deployer.owner));
console.log('\n');
console.log('Current block:', block.number);
}
function loadBaseAddresses() internal override {
lensHub = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LensHubProxy')));
vm.label(lensHub, 'LensHub');
console.log('Lens Hub Proxy: %s', lensHub);
legacyCollectNFTImpl = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LegacyCollectNFTImpl')));
vm.label(legacyCollectNFTImpl, 'LegacyCollectNFTImpl');
console.log('LegacyCollectNFTImpl: %s', legacyCollectNFTImpl);
moduleRegistry = json.readAddress(string(abi.encodePacked('.', targetEnv, '.ModuleRegistry')));
vm.label(moduleRegistry, 'ModuleRegistry');
console.log('ModuleRegistry: %s', moduleRegistry);
PROFILE_GUARDIAN_COOLDOWN = json.readUint(
string(abi.encodePacked('.', targetEnv, '.LensProfilesGuardianTimelock'))
);
console.log('PROFILE_GUARDIAN_COOLDOWN: %s', PROFILE_GUARDIAN_COOLDOWN);
HANDLE_GUARDIAN_COOLDOWN = json.readUint(
string(abi.encodePacked('.', targetEnv, '.LensHandlesGuardianTimelock'))
);
console.log('HANDLE_GUARDIAN_COOLDOWN: %s', HANDLE_GUARDIAN_COOLDOWN);
lensHandlesAddress = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LensHandles')));
tokenHandleRegistryAddress = json.readAddress(string(abi.encodePacked('.', targetEnv, '.TokenHandleRegistry')));
Module[] memory followModules = abi.decode(
vm.parseJson(json, string(abi.encodePacked('.', targetEnv, '.Modules.v1.follow'))),
(Module[])
);
legacyFeeFollowModule = findModuleHelper(followModules, 'FeeFollowModule').addy;
vm.label(legacyFeeFollowModule, 'LegacyFeeFollowModule');
console.log('Legacy Fee Follow Module: %s', legacyFeeFollowModule);
legacyProfileFollowModule = findModuleHelper(followModules, 'ProfileFollowModule').addy;
vm.label(legacyProfileFollowModule, 'LegacyProfileFollowModule');
console.log('Legacy Profile Follow Module: %s', legacyProfileFollowModule);
followModules = abi.decode(
vm.parseJson(json, string(abi.encodePacked('.', targetEnv, '.Modules.v2.follow'))),
(Module[])
);
newFeeFollowModule = findModuleHelper(followModules, 'FeeFollowModule').addy;
vm.label(newFeeFollowModule, 'NewFeeFollowModule');
console.log('New Fee Follow Module: %s', newFeeFollowModule);
governanceContract = LensHubInitializable(lensHub).getGovernance();
governanceAdmin = Governance(governanceContract).owner();
lensHandlesOwner = governanceAdmin;
vm.label(lensHandlesOwner, 'LensHandlesOwner');
console.log('LensHandlesOwner: %s', lensHandlesOwner);
followNFTImpl = json.readAddress(string(abi.encodePacked('.', targetEnv, '.FollowNFTImpl')));
vm.label(followNFTImpl, 'FollowNFTImpl');
console.log('FollowNFTImpl: %s', followNFTImpl);
lensHandlesImpl = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LensHandlesImpl')));
vm.label(lensHandlesImpl, 'LensHandlesImpl');
console.log('LensHandlesImpl: %s', lensHandlesImpl);
}
function deploy() internal {
if (lensHub == address(0)) {
console.log('LensHub not set');
revert('LensHub not set');
}
if (lensHandlesOwner == address(0)) {
console.log('lensHandlesOwner not set');
revert('lensHandlesOwner not set');
}
if (HANDLE_GUARDIAN_COOLDOWN == 0) {
console.log('HANDLE_GUARDIAN_COOLDOWN not set');
revert('HANDLE_GUARDIAN_COOLDOWN not set');
}
if (legacyCollectNFTImpl == address(0)) {
console.log('LegacyCollectNFTImpl not set');
revert('LegacyCollectNFTImpl not set');
}
if (moduleRegistry == address(0)) {
console.log('ModuleRegistry not set');
revert('ModuleRegistry not set');
}
if (PROFILE_GUARDIAN_COOLDOWN == 0) {
console.log('PROFILE_GUARDIAN_COOLDOWN not set');
revert('PROFILE_GUARDIAN_COOLDOWN not set');
}
console.log('PROFILE_GUARDIAN_COOLDOWN: %s', PROFILE_GUARDIAN_COOLDOWN);
// Pass all the fucking shit and deploy LensHub V2 Impl with:
vm.startBroadcast(_deployer.ownerPk);
lensHubV2Impl = address(
new LensHubInitializable(
followNFTImpl,
legacyCollectNFTImpl,
moduleRegistry,
PROFILE_GUARDIAN_COOLDOWN,
Types.MigrationParams({
lensHandlesAddress: lensHandlesAddress,
tokenHandleRegistryAddress: tokenHandleRegistryAddress,
legacyFeeFollowModule: legacyFeeFollowModule,
legacyProfileFollowModule: legacyProfileFollowModule,
newFeeFollowModule: newFeeFollowModule
})
)
);
vm.stopBroadcast();
console.log('"arguments": [');
console.log('\t"%s"', followNFTImpl);
console.log('\t"%s"', legacyCollectNFTImpl);
console.log('\t"%s"', moduleRegistry);
console.log('\t"%s"', PROFILE_GUARDIAN_COOLDOWN);
console.log(
'\t"%s"',
string.concat(
'(',
vm.toString(lensHandlesAddress),
', ',
vm.toString(tokenHandleRegistryAddress),
', ',
vm.toString(legacyFeeFollowModule),
', ',
vm.toString(legacyProfileFollowModule),
', ',
vm.toString(newFeeFollowModule),
')'
)
);
console.log(']');
vm.writeLine(addressesFile, string.concat('LensHubV2Impl: ', vm.toString(lensHubV2Impl)));
saveContractAddress('LensHubV2Impl', lensHubV2Impl);
console.log('LensHubV2Impl: %s', lensHubV2Impl);
}
function run(string memory targetEnv_) external {
targetEnv = targetEnv_;
loadJson();
checkNetworkParams();
loadBaseAddresses();
loadPrivateKeys();
deploy();
}
}

View File

@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {ForkManagement} from 'script/helpers/ForkManagement.sol';
import 'forge-std/Script.sol';
import {LibString} from 'solady/utils/LibString.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
import {Governance} from 'contracts/misc/access/Governance.sol';
import {PermissionlessCreator} from 'contracts/misc/PermissionlessCreator.sol';
import {TransparentUpgradeableProxy} from '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';
import {LensHubInitializable} from 'contracts/misc/LensHubInitializable.sol';
contract DeployPermissionlessCreator is Script, ForkManagement {
using stdJson for string;
struct LensAccount {
uint256 ownerPk;
address owner;
uint256 profileId;
}
LensAccount _deployer;
string mnemonic;
uint256 internal PROFILE_GUARDIAN_COOLDOWN;
uint256 internal HANDLE_GUARDIAN_COOLDOWN;
address lensHub;
address lensHandlesAddress;
address tokenHandleRegistryAddress;
address governanceContract;
address governanceAdmin;
address proxyAdminContractAdmin;
address permissionlessCreatorImpl;
address permissionlessCreator;
string addressesFile = 'addressesV2.txt';
// TODO: Use from test/ContractAddresses
struct Module {
address addy;
string name;
}
// TODO: Move this somewhere common (also in UpgradeForkTest)
function findModuleHelper(
Module[] memory modules,
string memory moduleNameToFind
) internal pure returns (Module memory) {
for (uint256 i = 0; i < modules.length; i++) {
if (LibString.eq(modules[i].name, moduleNameToFind)) {
return modules[i];
}
}
revert('Module not found');
}
function saveContractAddress(string memory contractName, address deployedAddress) internal {
// console.log('Saving %s (%s) into addresses under %s environment', contractName, deployedAddress, targetEnv);
string[] memory inputs = new string[](5);
inputs[0] = 'node';
inputs[1] = 'script/helpers/saveAddress.js';
inputs[2] = targetEnv;
inputs[3] = contractName;
inputs[4] = vm.toString(deployedAddress);
// bytes memory res =
vm.ffi(inputs);
// string memory output = abi.decode(res, (string));
// console.log(output);
}
function loadPrivateKeys() internal {
if (isEnvSet('MNEMONIC')) {
mnemonic = vm.envString('MNEMONIC');
}
if (bytes(mnemonic).length == 0) {
revert('Missing mnemonic');
}
console.log('\n');
(_deployer.owner, _deployer.ownerPk) = deriveRememberKey(mnemonic, 0);
// console.logBytes32(bytes32(_deployer.ownerPk));
console.log('Deployer address: %s', address(_deployer.owner));
console.log('\n');
console.log('Current block:', block.number);
}
function loadBaseAddresses() internal override {
lensHub = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LensHubProxy')));
vm.label(lensHub, 'LensHub');
console.log('Lens Hub Proxy: %s', lensHub);
lensHandlesAddress = json.readAddress(string(abi.encodePacked('.', targetEnv, '.LensHandles')));
tokenHandleRegistryAddress = json.readAddress(string(abi.encodePacked('.', targetEnv, '.TokenHandleRegistry')));
governanceContract = LensHubInitializable(lensHub).getGovernance();
vm.label(governanceContract, 'Governance');
console.log('Governance Contract: %s', governanceContract);
governanceAdmin = Governance(governanceContract).owner();
vm.label(governanceAdmin, 'GovernanceAdmin');
console.log('Governance Contract Admin: %s', governanceAdmin);
proxyAdminContractAdmin = json.readAddress(
string(abi.encodePacked('.', targetEnv, '.ProxyAdminContractAdmin'))
);
vm.label(proxyAdminContractAdmin, 'ProxyAdminContractAdmin');
console.log('ProxyAdmin Contract Admin: %s', proxyAdminContractAdmin);
}
function deploy() internal {
if (lensHub == address(0)) {
console.log('LensHub not set');
revert('LensHub not set');
}
if (proxyAdminContractAdmin == address(0)) {
console.log('ProxyAdminContractAdmin not set');
revert('ProxyAdminContractAdmin not set');
}
if (lensHandlesAddress == address(0)) {
console.log('lensHandlesAddress not set');
revert('lensHandlesAddress not set');
}
if (tokenHandleRegistryAddress == address(0)) {
console.log('tokenHandleRegistryAddress not set');
revert('tokenHandleRegistryAddress not set');
}
vm.startBroadcast(_deployer.ownerPk);
permissionlessCreatorImpl = address(
new PermissionlessCreator(governanceAdmin, lensHub, lensHandlesAddress, tokenHandleRegistryAddress)
);
// Make PermissionlessCreator a transparentUpgradeableProxy
// permissionlessCreator = address(
// new TransparentUpgradeableProxy(permissionlessCreatorImpl, proxyAdminContractAdmin, '')
// );
vm.stopBroadcast();
console.log('\n\n---------------------------------------------------');
console.log('PermissionlessCreator params:');
console.log('\towner: %s', governanceAdmin);
console.log('\tlensHub: %s', lensHub);
console.log('\tlensHandlesAddress: %s', lensHandlesAddress);
console.log('\ttokenHandleRegistryAddress: %s', tokenHandleRegistryAddress);
console.log('\n\n---------------------------------------------------');
console.log('PermissionlessCreator TransparentProxy params:');
console.log('\timplementation: %s', permissionlessCreatorImpl);
console.log('\tadmin: %s', proxyAdminContractAdmin);
vm.writeLine(
addressesFile,
string.concat('PermissionlessCreatorImpl: ', vm.toString(permissionlessCreatorImpl))
);
saveContractAddress('PermissionlessCreatorImpl', permissionlessCreatorImpl);
console.log('\n\nPermissionlessCreatorImpl: %s', permissionlessCreatorImpl);
vm.writeLine(addressesFile, string.concat('PermissionlessCreator: ', vm.toString(permissionlessCreator)));
saveContractAddress('PermissionlessCreator', permissionlessCreator);
console.log('PermissionlessCreator: %s', permissionlessCreator);
}
function run(string memory targetEnv_) external {
targetEnv = targetEnv_;
loadJson();
checkNetworkParams();
loadBaseAddresses();
loadPrivateKeys();
deploy();
}
}

View File

@@ -0,0 +1,27 @@
source .env
set -e
TARGET=$1
if [[ "$TARGET" == "" ]]
then
echo "No TARGET specified. Terminating"
exit 1
fi
echo "Using target: $TARGET"
IMPLEMENTATION_SLOT="0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
LENSHUB=$(node script/helpers/readAddress.js $TARGET LensHubProxy)
PROXY_CONTRACT=$(node script/helpers/readAddress.js $TARGET ProxyAdminContract)
PROXY_CONTRACT_OWNER=$(cast call $PROXY_CONTRACT "owner()(address)")
cast rpc anvil_impersonateAccount $PROXY_CONTRACT_OWNER
cast send $PROXY_CONTRACT "proxy_upgrade(address)" "0xb4A26f55Cc2d1473b8A7649d90d34ba52A480391" --unlocked --from $PROXY_CONTRACT_OWNER
NEW_IMPLEMENTATION=$(cast parse-bytes32-address $(cast storage $LENSHUB $IMPLEMENTATION_SLOT))
echo "Successfully upgraded LensHub to $NEW_IMPLEMENTATION"
cast rpc anvil_stopImpersonatingAccount $PROXY_CONTRACT_OWNER

View File

@@ -39,7 +39,7 @@ CALLDATA=$(cast calldata "run(string)" $TARGET)
echo "Interactions calldata:"
echo "$CALLDATA"
forge script script/$SCRIPT_NAME.s.sol:$SCRIPT_NAME -s $CALLDATA --rpc-url $NETWORK -vv --legacy --skip test --ffi
forge script script/$SCRIPT_NAME.s.sol:$SCRIPT_NAME --sig $CALLDATA --rpc-url $NETWORK -vv --legacy --skip test --ffi
# If the confirmation override is set to s or S - then we skip the rest of the script and exit with success
if [[ "$CONFIRMATION_OVERRIDE" == "s" || "$CONFIRMATION_OVERRIDE" == "S" ]]
@@ -83,10 +83,10 @@ if [[ "$CONFIRMATION" == "y" || "$CONFIRMATION" == "Y" ]]
NETWORK="matic"
fi
catapulta script script/$SCRIPT_NAME.s.sol --chain $NETWORK -s $CALLDATA --legacy --skip test --ffi --slow --skip-git
catapulta script script/$SCRIPT_NAME.s.sol --chain $NETWORK --sig $CALLDATA --legacy --skip test --ffi --slow --skip-git
exit 0
else
forge script script/$SCRIPT_NAME.s.sol:$SCRIPT_NAME -s $CALLDATA --rpc-url $NETWORK -vv --legacy --skip test --ffi --slow --broadcast
forge script script/$SCRIPT_NAME.s.sol:$SCRIPT_NAME --sig $CALLDATA --rpc-url $NETWORK -vv --legacy --skip test --ffi --slow --broadcast
fi
else

View File

@@ -7,6 +7,8 @@
# (It is normal for the numbers in end of type names to be different)
source .env
set -e
if [[ $(colordiff --help 2>/dev/null) ]]
then
shopt -s expand_aliases
@@ -65,10 +67,10 @@ if [[ $NETWORK == "" ]]
exit 1
fi
if [[ $NETWORK == "mumbai" ]]
then
NETWORK="polygon-mumbai"
fi
# if [[ $NETWORK == "mumbai" ]]
# then
# NETWORK="polygon-mumbai"
# fi
PROXY_ADDRESS=$(node script/helpers/readAddress.js $TARGET ${PROXY_NAME})
NEW_IMPLEMENTATION_ADDRESS=$(node script/helpers/readAddress.js $TARGET ${NEW_IMPL_NAME})

View File

@@ -0,0 +1,499 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import 'test/base/BaseTest.t.sol';
import 'test/LensBaseERC721Test.t.sol';
import {Base64} from 'solady/utils/Base64.sol';
import {LibString} from 'solady/utils/LibString.sol';
import {ProfileTokenURI} from 'contracts/misc/token-uris/ProfileTokenURI.sol';
import {IProfileTokenURI} from 'contracts/interfaces/IProfileTokenURI.sol';
import {ILensProfiles} from 'contracts/interfaces/ILensProfiles.sol';
import {MockTokenHolderContract} from 'test/mocks/MockTokenHolderContract.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
interface IGuardedToken is IERC721 {
function DANGER__disableTokenGuardian() external;
function enableTokenGuardian() external;
function getTokenGuardianDisablingTimestamp(address wallet) external view returns (uint256);
function transferFromKeepingDelegates(address from, address to, uint256 tokenId) external;
function burn(uint256 tokenId) external;
}
contract TransferKeepingDelegatesTest is BaseTest {
using Address for address;
function _getERC721TokenAddress() internal view virtual returns (address) {
return address(hub);
}
function _LensProfiles() private view returns (ILensProfiles) {
return ILensProfiles(_getERC721TokenAddress());
}
function _mintERC721(address to) internal virtual returns (uint256) {
vm.assume(!_isLensHubProxyAdmin(to));
return _createProfile(to);
}
function _burnERC721(uint256 tokenId) internal virtual {
return hub.burn(tokenId);
}
function _disableGuardian(address wallet) internal {
_effectivelyDisableProfileGuardian(wallet);
}
function _assumeNotProxyAdmin(address account) internal view virtual {
vm.assume(!_isLensHubProxyAdmin(account));
}
function _TOKEN_GUARDIAN_COOLDOWN() internal view returns (uint256) {
return fork ? hub.TOKEN_GUARDIAN_COOLDOWN() : PROFILE_GUARDIAN_COOLDOWN;
}
function _guardedToken() private view returns (IGuardedToken) {
return IGuardedToken(_getERC721TokenAddress());
}
MockTokenHolderContract tokenHolderContract;
uint256 tokenIdHeldByEOA;
uint256 tokenIdHeldByNonEOA;
function setUp() public virtual override {
super.setUp();
tokenHolderContract = new MockTokenHolderContract();
tokenHolderContract.setCollection(address(_guardedToken()));
tokenIdHeldByEOA = _mintERC721(defaultAccount.owner);
tokenIdHeldByNonEOA = _mintERC721(address(this));
_guardedToken().safeTransferFrom(address(this), address(tokenHolderContract), tokenIdHeldByNonEOA);
}
// TokenGuardian tests
function testCannot_transferFrom_ifEOA_andTokenGuardianEnabled(address to) public {
vm.assume(to != address(0));
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
vm.prank(defaultAccount.owner);
vm.expectRevert(Errors.GuardianEnabled.selector);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA);
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner);
}
function testCannot_transferFrom_ifEOA_andTokenGuardianDisabled_butNotTakenEffectYet(
uint256 elapsedTimeAfterDisabling,
address to
) public {
vm.assume(to != address(0));
elapsedTimeAfterDisabling = bound(elapsedTimeAfterDisabling, 0, _TOKEN_GUARDIAN_COOLDOWN() - 1);
vm.prank(defaultAccount.owner);
_guardedToken().DANGER__disableTokenGuardian();
vm.warp(block.timestamp + elapsedTimeAfterDisabling);
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
vm.prank(defaultAccount.owner);
vm.expectRevert(Errors.GuardianEnabled.selector);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA);
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner);
}
function testTransferFrom_ifEOA_onlyAfterTokenGuardianIsEffectivelyDisabled(address to) public {
vm.assume(to != address(0));
_effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner);
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
vm.prank(defaultAccount.owner);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA);
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), to);
}
function testApprovalStateDoesNotChange_afterProtectionStateChanges(address anotherAddress) public {
vm.assume(anotherAddress != address(0));
vm.assume(anotherAddress != defaultAccount.owner);
// Disable protection
_effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner);
// Approve
vm.prank(defaultAccount.owner);
_guardedToken().approve(anotherAddress, tokenIdHeldByEOA);
// Approve state has changed
assertEq(_guardedToken().getApproved(tokenIdHeldByEOA), anotherAddress);
// Enable protection
vm.prank(defaultAccount.owner);
_guardedToken().enableTokenGuardian();
// Approve state remains the same after enabling protection
assertEq(_guardedToken().getApproved(tokenIdHeldByEOA), anotherAddress);
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
// But, you cannot transfer even if approved, because the protection is enabled
vm.prank(defaultAccount.owner);
vm.expectRevert(Errors.GuardianEnabled.selector);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA);
}
function testApproveForAllState_DoesNotChange_AfterGuardianStateChanges(address anotherAddress) public {
vm.assume(anotherAddress != address(0));
vm.assume(anotherAddress != defaultAccount.owner);
// Disable protection
_effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner);
// ApproveForAll
vm.prank(defaultAccount.owner);
_guardedToken().setApprovalForAll(anotherAddress, true);
// ApproveForAll state has changed
assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress));
// Enable protection
vm.prank(defaultAccount.owner);
_guardedToken().enableTokenGuardian();
// ApproveForAll state remains the same after enabling protection
assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress));
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
// But, you cannot transfer even if ApprovedForAll, because the protection is enabled
vm.prank(defaultAccount.owner);
vm.expectRevert(Errors.GuardianEnabled.selector);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA);
}
function testTransfersDoesNotAffectProtectionState_InboundTransfer(address anotherAddress) public {
vm.assume(anotherAddress != address(0));
vm.assume(anotherAddress != defaultAccount.owner);
vm.assume(anotherAddress.code.length == 0);
// UserTwo does not have any profile
vm.assume(_guardedToken().balanceOf(anotherAddress) == 0);
// User disables protection, so it can perform a transfer later
_effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner);
// UserTwo disables protection
_effectivelyDisableGuardian(address(_guardedToken()), anotherAddress);
// UserTwo ApproveForAll User
vm.prank(anotherAddress);
_guardedToken().setApprovalForAll(defaultAccount.owner, true);
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
// UserTwo receives a profile from User
vm.prank(defaultAccount.owner);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA);
// UserTwo now holds the profile
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), anotherAddress);
vm.prank(governance);
hub.whitelistProfileCreator(anotherAddress, true);
// The profile is unprotected, and User is ApproveForAll by UserTwo, so can transfer the profile back
vm.prank(anotherAddress);
_guardedToken().transferFromKeepingDelegates(anotherAddress, defaultAccount.owner, tokenIdHeldByEOA);
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner);
}
function testTransfersDoNotAffectProtectionState_OutboundTransfer(address anotherAddress) public {
vm.assume(anotherAddress != address(0));
vm.assume(anotherAddress != defaultAccount.owner);
// Disables protection
_effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner);
vm.prank(governance);
hub.whitelistProfileCreator(defaultAccount.owner, true);
// Transfers the profile to UserTwo
vm.prank(defaultAccount.owner);
_guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA);
// User does not have the profile anymore
assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), anotherAddress);
// Transfers does not affect protection state, so User can execute ApproveForAll even after transfer
vm.prank(defaultAccount.owner);
_guardedToken().setApprovalForAll(anotherAddress, true);
assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress));
}
// General ERC721.TransferFrom tests
function testTransferFromKeepingDelegates_SenderIsApproved(address owner, address approvedTo, address to) public {
vm.assume(owner != address(0));
vm.assume(approvedTo != address(0));
vm.assume(owner != approvedTo);
vm.assume(to != address(0));
uint256 tokenId = _mintERC721(owner);
_disableGuardian(owner);
vm.prank(owner);
_LensProfiles().approve(approvedTo, tokenId);
uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner);
uint256 toBalanceBefore = _LensProfiles().balanceOf(to);
_assumeNotProxyAdmin(approvedTo);
_disableGuardian(approvedTo);
vm.prank(governance);
hub.whitelistProfileCreator(approvedTo, true);
vm.prank(approvedTo);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner);
uint256 toBalanceAfter = _LensProfiles().balanceOf(to);
assertEq(_LensProfiles().getApproved(tokenId), address(0));
assertEq(_LensProfiles().ownerOf(tokenId), to);
if (owner != to) {
assertEq(ownerBalanceAfter, ownerBalanceBefore - 1);
assertEq(toBalanceAfter, toBalanceBefore + 1);
}
}
function testTransferFromKeepingDelegates_SenderIsTheOwner(address owner, address to) public {
vm.assume(owner != address(0));
vm.assume(to != address(0));
uint256 tokenId = _mintERC721(owner);
uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner);
uint256 toBalanceBefore = _LensProfiles().balanceOf(to);
_disableGuardian(owner);
vm.prank(governance);
hub.whitelistProfileCreator(owner, true);
vm.prank(owner);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner);
uint256 toBalanceAfter = _LensProfiles().balanceOf(to);
assertEq(_LensProfiles().getApproved(tokenId), address(0));
assertEq(_LensProfiles().ownerOf(tokenId), to);
if (owner != to) {
assertEq(ownerBalanceAfter, ownerBalanceBefore - 1);
assertEq(toBalanceAfter, toBalanceBefore + 1);
}
}
function testTransferFromKeepingDelegates_SenderIsApprovedForAll(
address owner,
address approvedTo,
address to
) public {
vm.assume(owner != address(0));
vm.assume(approvedTo != address(0));
vm.assume(owner != approvedTo);
vm.assume(to != address(0));
uint256 tokenId = _mintERC721(owner);
_disableGuardian(owner);
vm.prank(owner);
_LensProfiles().setApprovalForAll(approvedTo, true);
uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner);
uint256 toBalanceBefore = _LensProfiles().balanceOf(to);
_assumeNotProxyAdmin(approvedTo);
_disableGuardian(approvedTo);
vm.prank(governance);
hub.whitelistProfileCreator(approvedTo, true);
vm.prank(approvedTo);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner);
uint256 toBalanceAfter = _LensProfiles().balanceOf(to);
assertEq(_LensProfiles().ownerOf(tokenId), to);
if (owner != to) {
assertEq(ownerBalanceAfter, ownerBalanceBefore - 1);
assertEq(toBalanceAfter, toBalanceBefore + 1);
}
}
function testCannot_TransferFromKeepingDelegates_NotOwner(address owner, address to, address otherAddress) public {
vm.assume(owner != to);
vm.assume(owner != otherAddress);
vm.assume(to != address(0));
vm.assume(owner != address(0));
vm.assume(otherAddress != address(0));
uint256 tokenId = _mintERC721(owner);
_assumeNotProxyAdmin(otherAddress);
vm.prank(governance);
hub.whitelistProfileCreator(otherAddress, true);
vm.expectRevert(Errors.NotOwnerOrApproved.selector);
vm.prank(otherAddress);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
}
function testCannotTransferFromKeepingDelegates_WrongFromParameter_SenderOwner(
address owner,
address from,
address to
) public {
_assumeNotProxyAdmin(owner);
vm.assume(owner != to);
vm.assume(owner != from);
vm.assume(owner != address(0));
vm.assume(to != address(0));
uint256 tokenId = _mintERC721(owner);
vm.prank(governance);
hub.whitelistProfileCreator(owner, true);
vm.expectRevert(Errors.InvalidOwner.selector);
vm.prank(owner);
_LensProfiles().transferFromKeepingDelegates(from, to, tokenId);
}
function testCannot_TransferFromKeepingDelegates_NonexistingToken(
uint256 tokenId,
address from,
address to
) public {
vm.assume(from != address(0));
vm.assume(to != address(0));
vm.assume(_LensProfiles().exists(tokenId) == false);
vm.prank(governance);
hub.whitelistProfileCreator(address(this), true);
vm.expectRevert(Errors.TokenDoesNotExist.selector);
_LensProfiles().transferFromKeepingDelegates(from, to, tokenId);
}
function testCannot_TransferFromKeepingDelegates_ToZero(address owner) public {
vm.assume(owner != address(0));
uint256 tokenId = _mintERC721(owner);
vm.prank(governance);
hub.whitelistProfileCreator(owner, true);
vm.expectRevert(Errors.InvalidParameter.selector);
vm.prank(owner);
_LensProfiles().transferFromKeepingDelegates(owner, address(0), tokenId);
}
// Tests list for TransferFromKeepingDelegates function:
// Negatives
function testCannot_TransferFromKeepingDelegates_IfNotWhitelistedProfileCreator(
address owner,
address to,
address approvedTo
) public {
vm.assume(owner != to);
vm.assume(to != address(0));
vm.assume(owner != address(0));
vm.assume(approvedTo != address(0));
vm.assume(hub.isProfileCreatorWhitelisted(approvedTo) == false);
_assumeNotProxyAdmin(approvedTo);
_assumeNotProxyAdmin(owner);
_disableGuardian(owner);
uint256 tokenId = _mintERC721(owner);
if (owner != approvedTo) {
vm.prank(owner);
_LensProfiles().approve(approvedTo, tokenId);
}
vm.expectRevert(Errors.NotAllowed.selector);
vm.prank(approvedTo);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
}
// Scenarios
function testTransferFromKeepingDelegates(address owner, address to, address approvedTo) public {
vm.assume(owner != to);
vm.assume(to != address(0));
vm.assume(owner != address(0));
vm.assume(approvedTo != address(0));
_assumeNotProxyAdmin(approvedTo);
_disableGuardian(owner);
uint256 tokenId = _mintERC721(owner);
vm.prank(governance);
hub.whitelistProfileCreator(approvedTo, true);
if (owner != approvedTo) {
vm.prank(owner);
_LensProfiles().approve(approvedTo, tokenId);
}
address[] memory delegatedExecutors = new address[](3);
delegatedExecutors[0] = makeAddr('DE0');
delegatedExecutors[1] = makeAddr('DE1');
delegatedExecutors[2] = makeAddr('DE2');
// Initialize an array of bools with the same length as delegatedExecutors
bool[] memory executorEnabled = new bool[](delegatedExecutors.length);
// Fill the array with `true`
for (uint256 i = 0; i < delegatedExecutors.length; i++) {
executorEnabled[i] = true;
}
vm.prank(owner);
hub.changeDelegatedExecutorsConfig(tokenId, delegatedExecutors, executorEnabled);
vm.prank(approvedTo);
_LensProfiles().transferFromKeepingDelegates(owner, to, tokenId);
for (uint256 i = 0; i < delegatedExecutors.length; i++) {
assertTrue(hub.isDelegatedExecutorApproved(tokenId, delegatedExecutors[i]));
}
}
}

View File

@@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import 'test/base/BaseTest.t.sol';
import {PermissionlessCreator} from 'contracts/misc/PermissionlessCreator.sol';
import {FreeCreditsProvider} from 'contracts/misc/FreeCreditsProvider.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
contract FreeCreditsProviderTest is BaseTest {
error OnlyOwner();
using stdJson for string;
PermissionlessCreator permissionlessCreator;
address permissionlessCreatorOwner = makeAddr('PERMISSIONLESS_CREATOR_OWNER');
FreeCreditsProvider freeCreditsProvider;
function setUp() public virtual override {
super.setUp();
if (fork) {
if (keyExists(json, string(abi.encodePacked('.', forkEnv, '.PermissionlessCreator')))) {
permissionlessCreator = PermissionlessCreator(
json.readAddress(string(abi.encodePacked('.', forkEnv, '.PermissionlessCreator')))
);
permissionlessCreatorOwner = permissionlessCreator.OWNER();
} else {
console.log('PermissionlessCreator key does not exist');
if (forkVersion == 1) {
console.log('No PermissionlessCreator address found - deploying new one');
permissionlessCreator = new PermissionlessCreator(
permissionlessCreatorOwner,
address(hub),
address(lensHandles),
address(tokenHandleRegistry)
);
} else {
console.log('No PermissionlessCreator address found in addressBook, which is required for V2');
revert('No PermissionlessCreator address found in addressBook, which is required for V2');
}
}
} else {
permissionlessCreator = new PermissionlessCreator(
permissionlessCreatorOwner,
address(hub),
address(lensHandles),
address(tokenHandleRegistry)
);
vm.startPrank(permissionlessCreatorOwner);
permissionlessCreator.setHandleCreationPrice(5 ether);
permissionlessCreator.setProfileCreationPrice(5 ether);
permissionlessCreator.setHandleLengthMin(5);
vm.stopPrank();
}
vm.prank(governance);
hub.whitelistProfileCreator(address(permissionlessCreator), true);
freeCreditsProvider = new FreeCreditsProvider(address(permissionlessCreator));
vm.prank(permissionlessCreatorOwner);
permissionlessCreator.addCreditProvider(address(freeCreditsProvider));
}
// Scenarios
function testIncreaseCredit(address profileCreator, address txSender) public {
vm.assume(profileCreator != address(0));
vm.assume(profileCreator != address(permissionlessCreator));
vm.assume(profileCreator != address(freeCreditsProvider));
vm.assume(txSender != address(0));
vm.assume(txSender != address(permissionlessCreator));
vm.assume(txSender != address(freeCreditsProvider));
uint256 creditsBefore = permissionlessCreator.getCreditBalance(profileCreator);
vm.prank(txSender);
freeCreditsProvider.getFreeCredit(profileCreator, 123);
uint256 creditsAfter = permissionlessCreator.getCreditBalance(profileCreator);
assertEq(creditsBefore + 123, creditsAfter);
}
function testDecreaseCredit(address profileCreator) public {
vm.assume(profileCreator != address(0));
vm.assume(profileCreator != address(permissionlessCreator));
vm.assume(profileCreator != address(freeCreditsProvider));
freeCreditsProvider.getFreeCredit(profileCreator, 123456);
uint256 creditsBefore = permissionlessCreator.getCreditBalance(profileCreator);
vm.prank(profileCreator);
freeCreditsProvider.burnCredits(123);
uint256 creditsAfter = permissionlessCreator.getCreditBalance(profileCreator);
assertEq(creditsBefore - 123, creditsAfter);
}
}

File diff suppressed because it is too large Load Diff